CStdioFile的Writestring写入中文的总结(unicode-ucs2/utf8-无Bom/ansi-gb2312)

时间:2021-08-27 20:20:48

参考:

1. http://blog.csdn.net/sun20082567/article/details/8316625

2. http://blog.csdn.net/gillerr/article/details/8518495 等)

Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu  转载请标明来源 

 

 (注:字节序为little-endian  更新20130809)

关于CString的字符串结束符

使用Writestring的话和TCHAR类型相关性非常强,我们来看看CString:

       1. 先讨论结字符串的结束符,对于char型的话是        '\0'     (0x00)

                                                         对于wchar_t型的话是 '\0'\0' (0x00 0x00)

        2. 我们知道CString在不同环境编译出来,可能是char型,或是wchar_t型 (我们使用TCHAR来统一标识)

        从上面两点我们能看出,CString存储时,其结束符号的判断(ReleaseBuffer(), GetLength())我们可以理解为和char或wchar_t一致

如果不考虑结束符号(不使用CString字符串方法),我们可以把CString当成我们申请的一段内存进行灵活的使用(执行GetBuffer(size)申请的内存,而不执行ReleaseBuffer判断TCHAR字符串结束) 。

 

 

下面讨论的前提TCHAR为wchar_t:

(TCHAR为char时按utf8/ansi存储是没有障碍的,存储unicode会有结束符识别的问题)

 (通常TCAR为wchar_t,像在vc80,vc90,vc100中)  (vc60, vc70中TCHAR通常使用的是char型)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

先写给出的开发建议,然后再来详细介绍:

不建议使用(TypeText):使用Writestring写入时,文件打开模式为TypeText时只会写入TCHAR双字节的一个字节。需要把存储的信息,每一个字节扩展一个00字节(扩展仅仅支持中文情况,英文的话,0x0000 会被当成结束符号;中文的话,TCHAR两个字节都不为0x00,所以扩展也基本可以)。这样写入到文件的内容才会是我们想要的内容。

 

建议使用(TypeBinary):使用Writestring写入时,文件打开模式为typeBinary时,没有这个问题,TCHAR的两个字符都会写入到文件中。

 

不建议使用(ANSI/UFT8-WriteString):同时有中英文,以ANSI/UTF8为文件格式时,不建议使用WriteString. 因为这两个格式英文单字节,汉字双字节/三字节,整体大小可能为Byte的单数倍。Byte单数倍使用CString存储时,末尾字符只有一个字符,补充了0x00,写入文件多一个0x00。

 

建议使用(Unicode-WriteString):同时有中英文,以ANSI/UTF8为存储格式时,建议使用Write写到文件中。Unicode存储格式时,中英文均为双字节,可以使用typeBinary格式,使用WirteString写入.

 

 TypeText打开时(unicode格式),例如想要写入 85 51 99 84 E5 53(内蒙古),则要扩展成85 00 51 00 99 00 84 00 E5 00 53 00,也或者使用write例如:

(使用Writestring写入时,文件打开模式为TypeText时只会写入TCHAR双字节的一个字节。)

CStdioFile unicodeFile(_T("unicode.txt"), CFile::modeCreate|CFile::typeText|CFile::modeWrite);

WORD unicodeFlag = 0xFEFF; //文件采用unicode格式

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD));

unicodeFile.Write(_T("内蒙古abc"), sizeof(_T("内蒙古abc"));  

(typeText模式时,使用WriteString写英文+中文这种格式,如“abc内蒙古”会有问题-只写TCHAR两个字符的一个,见最上面的描述;使用Write则没有问题)

 

可以使用typeBinary来写(unicode格式),就不用扩展了,例如

CStdioFile unicodeFile(_T("unicode.txt"), CFile::modeCreate|CFile::typeBinary|CFile::modeWrite);

WORD unicodeFlag = 0xFEFF; //文件采用unicode格式

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD));

unicodeFile.WriteString(_T("内蒙古abc"));

 

 

以下部分是使用文件格式不同时(unicode-ucs2/utf8/ansi)的情况,都是是可以写入中文成功的:

-----先看不同字符集下的编码对比;

unicode-ucs2字符集:每个字符/汉字 占 两个字节(2 Bytes)。实例如下:

CString cstrName(_T("内蒙古"));

cstrName存储的Hex信息是:85 51 99 84 E5 53

 

utf8字符集:每个汉字通常占3个字节(3 Bytes).,每个字符占一个字节(1 Byte)。实例如下:

通常从utf8格式文件中Read时的得到的字符:

utf8文件中的汉字“内蒙古”的编码HEX是:E5 86 85   E8 92 99   E5 8F A4

 

ANSI时(gb2312):每个字符占1个字节,每个汉字占两个字节。实例如下:

char buffer[] = "内蒙古"

buffer存储的Hex信息是:C4 DA C3 C9 B9 C5

 

再回过头来看这个问题:

如何把 ("abc内蒙古")使用CFile.WriteString写入到文件中

方法有多中,基于存储格式的不同,转换目标格式的不同,方法也不同:

首先我们明白一点:中文在ANSI/UNICODE/UTF8 情况,编码正常时(如上边"内蒙古"在不同格式的Hex信息),在UE/Notepad++中都是可以展示的

-----转成ANSI格式显示// 注意文件需要为ANSI格式,否则展示会不OK

(typeBinary模式打开的模式,不需要扩展字节情况)

1. 获取ANSI格式

char buffer[100] = "abc内蒙古";  // ANSI格式,长度为7字节为0x61 62 63 C4 DA C3 C9 B9 C5

2. 以ANSI格式转到TCHAR字符串中,并存入到CString(使用强制转,强制转的话,需要补充'\0')

//字符串的size不为2的整数倍,保存在CString里,需要手动多加一个结束符号'\0',而'\0'写入文件会造文件内容有问题(形如:notepad++打开时是"abc内蒙古NUL", notepad打开时是多一个空格,"abc内蒙古 ")  CString cstrName;  char *pChar = (char*)(LPCSTR)cstrName.GetBuffer(strlen(buffer) + 3);
 strcpy(pChar, buffer);
 pChar[strlen(buffer) + 1] ='\0';
 pChar[strlen(buffer) + 2] ='\0';
 cstrName.ReleaseBuffer();
 ansiFile.WriteString(cstrName);  // ANSI的size不为2的整数倍,(形如:notepad++打开时是"abc内蒙古NUL", notepad打开时是多一个空格,"abc内蒙古 ")
  //使用WriteString写入到文件中是有点问题的,使用write来写没有上面这个问题
file.write((void*)buffer, sizeof(buffer));

 

-----转为Unicode格式显示 // 注意文件需要为unicode格式,文件前两个字节为 FF FE

(typeBinary模式打开的模式,不需要扩展字节情况)

(typeText模式时,abc+中文这种格式,a需要补TCHAR的空格,存储到CString中会有问题,单纯中文的话还可以扩展一下)

1. 首先我们获取到unicode格式

CString cstrName ( _T("abc内蒙古"));  

2. 确保文件头必须为 文件前两个字节为 FF FE (little endian)(标示文件已unicode解析) 

WORD unicodeFlag = 0xFEFF;

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD)); 

4. 再写入字符串就OK了(typeBinary模式打开的模式,不需要扩展字节)

   file.WriteString(cstrName);

  

-----转成UTF8格式显示的时候 //注意文件为UTF8格式,否则展示会不OK

(typeBinary模式打开的模式,不需要扩展字节情况)

1. 首先我们获取到unicode格式

     CString cstrName ( _T("abc内蒙古"));   // ANSI(3+2*2=7Byte)-->Unicode(5*2=10Byte)  // 给CString初始时,会调用MultiByteToWideChar

 2. 然后unicode转utf8格式

   使用(注意第一个参数使用CP_UTF8

   ::WideCharToMultiByte(CP_UTF8, ..

3. 再写入字符串就OK了

(注意:这个和ANSI有一样的问题,建议使用Write来写)

   file.WriteString(cstrName);

 

 附两个格式转换的样例:

ANSI To Unicode

// CP_ACP  ANSI与Unicode之间转换。
// CP_UTF8 UTF-8与Unicode之间转换。
// 函数MultiByteToWideChar返回的长度包括了空格的长度
int unicodeLen = ::MultiByteToWideChar(CP_ACP,
0,
(char*)(LPCTSTR)cstr,
-1,
NULL,
0);
wchar_t* pUnicode = (wchar_t*)(LPCTSTR)(cstrOutput.GetBuffer((unicodeLen + 1) * 2));
::MultiByteToWideChar(CP_ACP,
0,
(char*)(LPCTSTR)cstr,
-1,
pUnicode,
unicodeLen);


 

UnicodeToANSI 

int  iANSILen = ::WideCharToMultiByte(CP_ACP,
0,
(LPCWSTR)(LPCTSTR)cstr,
-1,
NULL,
0,
NULL,
0);
char* pANSI = (char*)(LPCTSTR)(cstrOutout.GetBuffer(iANSILen + 1));
::WideCharToMultiByte(CP_ACP,
0,
(LPCWSTR)(LPCTSTR)cstr,
-1,
pANSI,
iANSILen,
NULL,
0);


 

 读本文之后,觉得有用的话可以继续读一下:

1. 涉及多平台版本的中英文字符文件读写和转换   :http://blog.csdn.net/chunyexiyu/article/details/11229547

2. 使用ReadString读取时的注意事项:http://blog.csdn.net/chunyexiyu/article/details/9001158

 

 Owned by  <春夜喜雨的专栏:http://blog.csdn.net/chunyexiyu>