参考:
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>