abc汉字
wstring
class
汉字与字符
abc汉字
abc汉字
wstring
class
汉字与字符
abc汉字
请按任意键继续. . .
6.std::ifstream 读取 ANSI 编码正常,std::wifstream 读取 ANSI 编码错误(也正常)…默认 locale("C") 不能识别中文字符(可以识别中文字符,当做C风格字符串)
std::wifstream 设置 imbue(locale("")) 或 locale(".936") 后正常读取(不能正确读取)。936 为 GB2312 的代码页。
实验 04:
test.txt 为 Shift-JIS 编码,内容为
うみねこのなく頃に
程序代码同实验3
ifstream 输出为
偆傒偹偙偺側偔崰偵
wifstream 设定 imbue(locale("")) 后输出相同
7.显而易见的,其他地区的编码无法正确识别。这也是很多日本游戏和文本文件运行
或读取时产生乱码的原因。
实验 05:
test.txt 为 Shift-JIS 编码,内容同上
ifstream 与 wifstream 都添加 imbue(locale("jpn")) 或 locale(".932")
932 为 Shift-JIS 的代码页
偆傒偹偙偺側偔崰偵
うみねこのなく頃に
8.这里可以看出一个显著性差异。wifstream 在读取时按照 Shift-JIS 编码将其转换为
Unicode 储存,在 wcout 输出时又按照 ANSI (GB2312) 转换,其结果是 —— 正确显示
了其他地区编码的字符。而 ifstream 与 cout 则缺少那两步转换,结果与上例相同
以后的实验将不再考虑 ifstream 而只实验 wifstream。
实验 06:
test.txt 存为 UTF-16 编码(Win32 默认的 little endian),内容同上。
wifstream 设定为 imbue(locale(".1200"))
1200 为 UTF-16 的 code page
结果,运行出错…发现是 imbue(locale(".1200")); 这句的问题
试着将 ".1200" 改为 ".936" 则运行正常,输出乱码。(936是 GB2312 的代码页)
翻 MSDN 时在 Code Page 那页1200 UTF-16 后面发现一行小字:
"available only to managed applications"…郁闷
看来用 locale 转Unicode的想法到此结束了?记得 STL 书中貌似说过,locale 的名
字在各平台上是不统一的,因为关系到各平台的支持问题。这样的话,要么自己写
代码,要么就只好用 API 显式转换了:MultiByteToWideChar
另外,在 setlocale 函数说明中也写到,UTF-8 和 UTF-7 等每字符有可能大于2字节
的编码不被支持,所以 UTF-8 也只能用 MultiByteToWideChar 转咯…
目前大概只能得出结论 C++ STL locale 在 Win32 平台上支持不完善吧
实验 07: 用 API 重写读文件部分代码
#include<windows.h>
HANDLE hFile;
if(INVALID_HANDLE_VALUE != (hFile = CreateFileW(L"test.txt",
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))){
int iFileLength, iUniTest, i;
iFileLength = GetFileSize(hFile,NULL);
char *pBuffer, *pText;
pBuffer = new char[iFileLength+2];
DWORD dwBytesRead;
ReadFile(hFile,pBuffer,iFileLength,&dwBytesRead,NULL);
CloseHandle(hFile);
pBuffer[iFileLength] = ”;
pBuffer[iFileLength + 1] = ”;
iUniTest = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE;
if(IsTextUnicode(pBuffer,iFileLength,&iUniTest)){
pText = pBuffer + 2;
iFileLength -= 2;
if(iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE){
for(i = 0;i < iFileLength; i+=2)
swap(pText[i],pText[i+1]);
wstr = (wchar_t*)(pBuffer+2);
delete [] pBuffer;
wcout<<wstr<<‘\n’;
输出正确。以上程序段自动识别 Unicode 编码文件开头的 0xFFFE 标记判断是 Little Endian 还是
Big Endian 并做相应转换。但是代码量较大,且与 C++ 的 IO流 很不搭调…
9.可以看到,只是把输入内容去掉UTF-16开头的0xFFFE,直接把内存指针改为
wchar_t* 后 std::wstring 即可正确识别,说明程序中的宽字符存储格式实际上用的就是
UTF-16 little endian
实验 08:
不死心又去翻了 boost 库,发现 codecvt_null 这个好东西,看下实现是把文件存储内容
按照 wchar_t 为单位直接读入内存不做任何转换。这其实不正好是 UTF-16 需要做的么
以下把 test.txt 存为 UTF-16 little endian 再次实验
#include<boost/archive/codecvt_null.hpp>
wifstream wfin(L"test.txt");
locale utf16(loc, new boost::archive::codecvt_null<wchar_t>);
wfin.imbue(utf16);
while(wfin>>wstr){
wcout<<wstr<<endl;
wfin.close();
输出正确。
10. 看来可以把 codecvt_null 作为 UTF-16 的 codecvt_facet 读入 locale
来使用,避免使用类似上面 API 那么多代码。
实验 09:
将 test.txt 存为 UTF-16 Big Endian ,内容不变。程序不变
无法输出任何内容。
11. wcout 不认识 big endian 的 wchar_t …
看来想读取 UTF-16 Big Endian,仅靠 codecvt_null 还不够。稍微翻了一下
《C++ 输入输出流与本地化》这本书,现在可以考虑写一个自己的 codecvt_facet
了。有了 codecvt_null 的代码,稍作改动即可用于 UTF-16 big endian。虽说有了
现在的知识自己写个 utf-16 的codecvt_facet 也可以,但效率大概比不上 boost 里的。
代码准备:用类似的方法写出了自己的 codecvt_utf16 和 codecvt_utf16_reverse 两个
codecvt_facet…然后继续实验。自己写的内容放入咱自己的头文件吧:codecvt_utf.h,
内容加入自己的 namespace : tvt
实验 10: 用 codecvt_utf.h 代替 codecvt_null.hpp。用 codecvt_utf16 和
codecvt_utf16_reverse 实现 little endian 与 big endian 的输入。
wifstream wfin(L"test.txt");
locale utf16(loc,new tvt::codecvt_utf16<wchar_t>);
wfin.imbue(utf16);
while(wfin>>wstr){
wcout<<wstr<<endl;
wfin.close();