STL的I/O Stream如何读写Unicode文件

边城狂人 2007-01-08 05:20:11
据说要用到std::locale之类的东西
但是我不清楚啊,谁有办法或者例子?
要用STL解决不要纯C的方案
...全文
2060 40 打赏 收藏 转发到动态 举报
写回复
用AI写文章
40 条回复
切换为时间正序
请发表友善的回复…
发表回复
xins0123 2010-10-31
  • 打赏
  • 举报
回复
http://dev.firnow.com/course/3_program/c++/cppjs/20090403/163668.html

我也遇到这样的问题,这里说得比较详细点,参考一下
netxuning 2008-01-24
  • 打赏
  • 举报
回复
学习
heimu2257 2008-01-11
  • 打赏
  • 举报
回复
最近在写xml读写的问题,发现 xmlfile 是 utf-8 编码, 写出的文件中文乱码,在网络上查找到了一个 解决的方法 。 运行程序
在生成的 文本文件中 你另存为 可以看到 文本的格式 为 utf-8 所有的转换都在在ConvertGBKToUtf8 函数中做到了。
#include <iostream>
#include <string>
#include <fstream>
#include <afxwin.h>
using namespace std;

void ConvertGBKToUtf8(string &strGBK)
{
int len=MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK.c_str(), -1, NULL,0);
unsigned short * wszUtf8 = new unsigned short[len+1];
memset(wszUtf8, 0, len * 2 + 2);
MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK.c_str(), -1, wszUtf8, len);

len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
char *szUtf8=new char[len + 1];
memset(szUtf8, 0, len + 1);
WideCharToMultiByte (CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL,NULL);

strGBK = szUtf8;
delete[] szUtf8;
delete[] wszUtf8;

return;
}

void ConvertUtf8ToGBK(string &strUtf8)
{
int len=MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8.c_str(), -1, NULL,0);
unsigned short * wszGBK = new unsigned short[len+1];
memset(wszGBK, 0, len * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8.c_str(), -1, wszGBK, len);

len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
char *szGBK=new char[len + 1];
memset(szGBK, 0, len + 1);
WideCharToMultiByte (CP_ACP, 0, wszGBK, -1, szGBK, len, NULL,NULL);

strUtf8 = szGBK;
delete[] szGBK;
delete[] wszGBK;

return;
}

int main()
{
string test = "Hello! 您好!";
ConvertGBKToUtf8(test);

ofstream unicodefile("unicode.txt");

unicodefile << string(test);


return 0;

}

HewpKanXue 2007-01-21
  • 打赏
  • 举报
回复
你是说只用iostream,
问题是:已经有wiostream, 已经封装好的类可以用了。
如果你不用的话,一定要清楚知道UNICODE文件结构才行。
哪远远超出我的能力了。
iambic 2007-01-21
  • 打赏
  • 举报
回复
char换成wchar_t,其它的都自己搞定。
htqx 2007-01-21
  • 打赏
  • 举报
回复
我的意思也包含wiostream, 只要是这批io类
roger_77 2007-01-21
  • 打赏
  • 举报
回复
注意,上文仅仅适合于MSVC环境中STL库,对于STLPort有问题。
roger_77 2007-01-21
  • 打赏
  • 举报
回复
Wide 文件 I/O
这里是stream类的wide版本,它容易地定义t-风格的宏去管理他们:

你将像这样用它们:

tofstream testFile( "test.txt" ) ;
testFile << _T("ABC") ;
现在,你期待的结果是,当用single-byte 字符编译的时候,执行代码将生成3字节的文件,当用double-byte 字符编译的时候,执行代码将生成6字节的文件。但是你错了,都是3字节的文件。
到底怎么啦?

这渊源是标准C++的规定,wide流当写到 file。必须转换double-byte 到single-byte 。如上例,宽字符串L"ABC"(有6个字节长),当写到文件前,被转换成窄字符串(3字节)。更坏的情况,如何转换由库的实现来决定的( implementation-dependent)。

我不能找出一个确切的解释,为什么事情会弄成这样子。我猜测,文件被定义为考虑作为字符(single-byte)流。若允许同时写2字节的字符将无法提取。不管对还是错,这都导致严重的问题。例如,你不能写二进制数据到wofstream,因为这个类试图在输出前先窄字符化它。

这对我是明显的问题,因为我有大量的函数像这样写:

void outputStuff( tostream& os )
{
// output stuff to the stream
os << ....
}
假如你传递的是tstringstream 对象将没有问题(例如,它流出宽字符),但是假如你传递的是tofstream 将得到怪异的结果(因为所有内容都被窄化了)。

-----------------------
Wide 文件 I/O: 解决方案

用调试器单步跟踪STL,结果发现wofstream 在写输出到文件以前,调用std::codecvt 对象来窄化输出的数据。std::codecvt对象是造成字符串从一种字符集到另一种字符集转换的原因。C++要求作为标准提供:1、转换chars 到 chars(例如,费力地什么也不做),2、转换wchar_ts 到chars。后一种就是引起我们这么多伤心事的原因。

解决方案:写一个新的继承自codecvt的类,用来转换wchar_ts 到 wchar_ts(什么也不做),绑定到wofstream 对象中。当wofstream 试图转换它所输出的数据时,它将调用我们新的codecvt 对象,实际上什么也不做,不改变地写输出数据。


google groups浏览找一些P. J. Plauger写的代码 code (是MSVC环境中STL库的作者),但是用 Stlport 4.5.3 编译还是有问题。 这是最后敲定的版本:

#include

// nb: MSVC6+Stlport can't handle "std::"
// appearing in the NullCodecvtBase typedef.
using std::codecvt ;
typedef codecvt < wchar_t , char , mbstate_t > NullCodecvtBase ;

class NullCodecvt
: public NullCodecvtBase
{

public:
typedef wchar_t _E ;
typedef char _To ;
typedef mbstate_t _St ;

explicit NullCodecvt( size_t _R=0 ) : NullCodecvtBase(_R) { }

protected:
virtual result do_in( _St& _State ,
const _To* _F1 , const _To* _L1 , const _To*& _Mid1 ,
_E* F2 , _E* _L2 , _E*& _Mid2
) const
{
return noconv ;
}
virtual result do_out( _St& _State ,
const _E* _F1 , const _E* _L1 , const _E*& _Mid1 ,
_To* F2, _E* _L2 , _To*& _Mid2
) const
{
return noconv ;
}
virtual result do_unshift( _St& _State ,
_To* _F2 , _To* _L2 , _To*& _Mid2 ) const
{
return noconv ;
}
virtual int do_length( _St& _State , const _To* _F1 ,
const _To* _L1 , size_t _N2 ) const _THROW0()
{
return (_N2 < (size_t)(_L1 - _F1)) ? _N2 : _L1 - _F1 ;
}
virtual bool do_always_noconv() const _THROW0()
{
return true ;
}
virtual int do_max_length() const _THROW0()
{
return 2 ;
}
virtual int do_encoding() const _THROW0()
{
return 2 ;
}
} ;
你能看得出这些函数都是空架子,实际上什么也不做,仅仅返回noconv 指示而已。

剩下要做的仅仅是把其实例化,并连接到wofstream 对象中。用MSVC,假定你用_ADDFAC() 宏(非标准的)来imbue一个locale到对象。可是它不能和我的新的NullCodecvt类工作,因此我绕过这个宏,写一个新的来代替:

#define IMBUE_NULL_CODECVT( outputFile ) \
{ \
NullCodecvt* pNullCodecvt = new NullCodecvt ; \
locale loc = locale::classic() ; \
loc._Addfac( pNullCodecvt , NullCodecvt::id, NullCodecvt::_Getcat() ) ; \
(outputFile).imbue( loc ) ; \
}
好,上面给出的不能好好工作的例子代码,现在能这样写:

tofstream testFile ;
IMBUE_NULL_CODECVT( testFile ) ;
testFile.open( "test.txt" , ios::out | ios::binary ) ;
testFile << _T("ABC") ;
重要的是必须是在打开文件前,文件流对象要用新的codecvt对象imbue。文件也必须用binary模式打开。假如不是这种模式,每次文件看一个宽字符的高位或低位是10的时候,它将进行既定的CR/LF翻译,结果不是你想要的。假如你真的想要CR/LF序列,你可以明确地插入"\r\n"来代替std::endl。

====================================
参考:【翻译文章】如何升级基于STL的应用来支持Unicode-

http://dozb.bokee.com/1655050.html

femalelover 2007-01-21
  • 打赏
  • 举报
回复
好帖
htqx 2007-01-21
  • 打赏
  • 举报
回复

经过我的分析. wostream 这批 io 并没有实现(至少默认)unicode的读写.
而wstring 本身是unicode的.
wcout << wstring ; 到底做了什么?是将unicode 转化多字节(内码),然后显示出来.
wistream 能够将内码读取出来放在unicode,类似wcin,将屏幕的输入(内码)转化成unicode保存在wstring. 因此,也就是输入输出都是内码,只是进入程序中后的数据保存为unicode.

cout, cin这批只是没有经过这种转换,直接保存到string为内码罢了.

因为这种转化很郁闷,又难控制(自动转化), 所以我设计了用普通的io来读取保存字符流.只要字符流本身已经是Unicode, 那么读取和保存也就是Unicode了,不是么?

//示例

//保存unicode字符串
void saveW( ostream& out, wchar_t const* str, int size )
{
char const* pos = (char const*)str;
// wcout << str << " : " << size << endl;
char const* const utf16head = "\xFF\xFE";//txt文本用这个来标识字符编码
out.write( utf16head, 2 );
out.write( pos, size - 2 );

}


//调用的代码
wchar_t str[] = L"abc你好吗?";
ofstream out( "writeuft16_m.txt" );
saveW( out, str, sizeof( str ) );
out.close();


argenCHN 2007-01-20
  • 打赏
  • 举报
回复
up
学习
htqx 2007-01-20
  • 打赏
  • 举报
回复
iostream
HewpKanXue 2007-01-20
  • 打赏
  • 举报
回复
我也新学的啊,你说的C++的IO具体指什么啊,
htqx 2007-01-20
  • 打赏
  • 举报
回复
楼上的大哥,用c++的io如何处理,细节一点.
HewpKanXue 2007-01-20
  • 打赏
  • 举报
回复
终于学到了点东西,不容易啊!
#include <iostream>
#include <string>
#include <stdio.h>
#include <locale> ////
using namespace std;

//目标::建将文件小写字母,改成大写,写入另一个文件,//unicode文件
void main( )
{
FILE *f1;
FILE *f2;
//
setlocale(LC_ALL, ""); //重要,否则不能正常处理,出现乱码 setlocale(LC_ALL, "chs")也可以直接指定中国;
//locale loc ( "English" );
locale loc ( "Chinese" ); //or
wchar_t wc;
wchar_t wcstr[2]=L"好";
wstring sf1=L"C:\\Test.ini"; //该文件事先建好。
wstring sf2=L"C:\\Test.txt";
int fileOpen;
fileOpen=_wfopen_s(&f1,sf1.c_str(),L"rt+,ccs=UNICODE");
if (fileOpen!=0)// C4996 ///以读文本文件方式打开,字符指定为Unicode
// Note: _wfopen is deprecated; consider using _wfopen_s instead
{
wprintf(L"_wfopen failed!\n");
return ;
}
fileOpen=_wfopen_s(&f2,sf2.c_str(),L"wt+,ccs=UNICODE");
if (fileOpen!=0)// C4996 //write
// Note: _wfopen is deprecated; consider using _wfopen_s instead
{
wprintf(L"_wfopen failed!\n");
return ;
}

fpos_t pos; //文件位置
long ipos=0;
while(!feof(f1)) //判断的是否文件结束。
{
///read s char from f1 write to f2
fgetpos(f1 , &pos );//获取当前位置 ,用fsetpos(f1,&pos)定位。或fseek();
ipos=ftell(f1);
wc=getwc(f1);

if (islower(wc,loc)) {
wc=toupper(wc,loc);
}
fwprintf(stdout,L"%c",wc);//输出condole 结束符有问题,不明白;
putwc(wc,f2); //输出文件
}
cout<<endl;

wchar_t ws[100]=L"房间dkugdfu团体\n";
wstring ws1=L"sdjfg好了\n";

_putws(ws); //写字符穿到stdout
_putws(ws1.c_str());
fseek(f2,20L,SEEK_END);
fwprintf(f2,L"%s",ws); //在文件2上追加两个字符串,这里没问题。
fseek(f2,0,SEEK_END);
fwprintf(f2,L"%s",ws1.c_str());
fclose(f1);
fclose(f2);
system("pause");
}
htqx 2007-01-19
  • 打赏
  • 举报
回复
有点怀疑.
这个细节没人知道.
Jedimaster 2007-01-14
  • 打赏
  • 举报
回复
#define UNICODE
#define _UNICODE

VC工程属性里面设置为使用UNICODE

然后使用w开头的ANSI C函数或者是WND自己提供的读写UNICODE字符函数(推荐后者)
argenCHN 2007-01-12
  • 打赏
  • 举报
回复
up
htqx 2007-01-12
  • 打赏
  • 举报
回复
顶到高手们不好意思.
睡在床板下_ 2007-01-12
  • 打赏
  • 举报
回复
以前用过, 显示可以,但是保存读取就不成功
加载更多回复(19)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧