您的位置:首页 > 其它

关于 ifstream ofstream 的读写问题

2011-11-28 17:20 274 查看
问题现象(使用VS2008,UNICODE字符集):

wfstreamf; wstringpath=L"C:\\1.txt"; f.open(path.c_str(),ios::in);
打开文件后,读取文件内容正常,包括中文字符。

wofstreamf; wstringpath=L"C:\\1.txt";wstringstrText=L"test测试";f.open(path.c_str(),ios::out);f<<strText.c_str()<<std::endl;
打开文件后,无法写入中文字符。

解决方法:

wofstreamf; wstringpath=L"C:\\1.txt";wstringstrText=L"test测试";f.open(path.c_str(),ios::out); if(f.fail()) returnfalse; localeoldLocale=f.imbue(locale(locale(""),"",LC_CTYPE));f<<strText.c_str()<<std::endl; f.imbue(oldLocale); f.close();打开文件后,写入中文字符正常。
引申内容:

(1)iostreamlibrary参见http://www.cplusplus.com/reference/iostream/

(2)MBCS与UNICODE字符集原文链接http://blog.csdn.net/jsufcz/article/details/3514568

本文并不打算讲解Unicode的编码问题,因为本文主要对以下几个问题提一些见解:
1.MBCS多字节码的原理?
2.MBCS与Unicode的关系?
3.MBCS与Unicode的转换?
4.MBCS与Unicode的打印,乱码解决?

早在Windows采用Unicode统一编码进行语言管理之前,Windows为了能够进行非ANSI标准字符的输出,于是采用两个字节来表示这些语言文字。因为这些双字节文字和ANSI是混和在一起的,为了加以区别,Windows将这些字符的最高位置为1(即这些双字节文字的每个字节都>=127),所以这种表示法可以表示127x127约一万多种非ANSI文字,其本上可以表示任何一种语言的常用文字了。于是,Windows为每一个区域版本,都制定了分别独立的文字编码,这就是MBCS(多字节码)。

在采用Unicode之后,Windows仍然保留了MBCS技术,只不过它对每一种MBCS与Unicode建立了一种映射关系,当然这是通过Unicode的语言区域码实现的。windows对每个语言区域进行编号,并记录其范围。这样,只要给定这些区域编号,就可以实现任何MBCS与Unicode的转换。

在VS编程环境下,L""表示Unicode字符(请切记:WCHAR即ushort只表示宽字符,而宽字符并不就是unicode,反而Unicode属于宽字符),可喜的是,VS编译器直接将L""宏编译成了Unicode编码。我们可以使用%S等进行转换,如
setlocale(LC_ALL,"");//这句很重要,后面会讲

WCHARswzMsg[]=L"Unicode测试";
charszMsg[32]={0};
sprintf(szMsg,"%S",swzMsg);//这里%S表示进行MBCS/Unicode转换
反之,可以如下转:
WCHARswzMsg2[32]={0};
swprintf(swzMsg,L"%S",szMsg);

我们仔细分析上面字符串的长度和编码:
swzMsg:55006e00690063006f00640065004b6dd58b0000
szMsg:556e69636f6465b2e2cad400
注意到了没,swzMsg是Unicode编码的,其中文字"测试"部分是4b6bd58b,即"测"6b4b,"试"8bd5,可以看出是很接近的一段区域了吧。
而szMsg其中文部分是b2e2cad4,即“测"e2b2,"试"d4ca,跟上面分析的一样吧,四个字节都大于127,e2b2d4ca就是他们的MBCS码,
内码是(d2-127)(b2-127)(d4-127)(ca-127)。
Unicode字符串用wcslen()求长度,MBCS用strlen()求长度,原因我想大家都很清楚。

如果一个应用程序使用MBCS多字节编码,我们在中文环境下编译,再拿到韩文操作下去运行,会出现什么情况呢?肯定是乱码!
原因:中文环境下编译的MBCS中文字符被编译成了MBCS内码(小于127x127的连续码),而在韩文系统下,这些码可能对应了一些韩文字符,当然也有可能什么都没有。

其实,现代操作系统版本,如Windows内部已经用Unicode来表示各种文字编码了,它能识别任务它能表示的文字字符,
Unicode字符<->区域码起始值+MBCS内码
MBCS内码=(高位MBCS外码-127)<<8|(低位MBCS外码-127)

操作系统内还记录了区域码CodePage,如中文是.936,它对应了一个Unicode起始值。
任何一个Unicode字符,操作系统都可以根据区域码计算出其MBCS码。

OK,讲到这里,
setlocale(LC_ALL,"");
再次登场了,它表示设置区位码,""串表示使用当前的,如果不使用本语句,
wprintf(L"测试Unicode");
将不能正常显示,
sprintf(szMsg,"%S",swzMsg);
swprintf(swzMsg,L"%S",szMsg);
转换也会出现问题。

关于这点,我感到很奇怪,%S在进行MBCS/Unicode字符串转换和wprintf()在输出Unicode字符串的时候,为什么运行时刻库不能自已默认使用
setlocale(LC_ALL,"");
而L""又能使用默认区域码进行MBCS/Unicode转换,我想这应该是标准C时刻库和编译器版本之间的差异造成的吧。因此,
微软的WideCharToMultiByte和MultiByteToWideChar就做得好一些,我想它应该内部给我们调用了setlocale(LC_ALL,"")语句。
WCHARswzMsg[]=L"Unicode测试";
charszMBAA[12]={0};
::WideCharToMultiByte(CP_ACP,0,swzMsg,-1,szMBAA,sizeof(szMBAA),NULL,NULL);
printf("%s/n",szMBAA);
WCHARszWWAA[12]={0};
::MultiByteToWideChar(CP_ACP,0,szMBAA,-1,szWWAA,sizeof(szWWAA));

(3)ofstream和wofstream与中文输出问题参见http://archive.cnblogs.com/a/2174346/

我使用的是VS2008UNICODE字符集,但是验证了一下该文章中说的,存在几点问题:

1、第二项中不需要将全局locale恢复成原来的设置,并不会影响cout,wcout输出中文,并且printf,wprintf都能正常输出中文。

2、第三项中使用setlocale后,其他都正常,但是wcout不能输出中文,即使是像他说的恢复后,仍然不能输出中文。

关于CLocale,C++Locales可以参见http://stdcxx.apache.org/doc/stdlibug/24-3.html

但是还没有查出为什么用setlocale对于wcout输出中文没有效果(先记着)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: