参考文档:
http://msdn.microsoft.com/en-us/library/aa300688(v=vs.60).aspx
http://msdn.microsoft.com/zh-cn/library/5z097dxa(v=vs.90).aspx
http://msdn.microsoft.com/zh-cn/library/87zae4a3(v=vs.90).aspx
http://blog.csdn.net/masefee/article/details/6835688
http://blog.csdn.net/chunyexiyu/article/details/8938540
http://blog.csdn.net/chunyexiyu/article/details/9001158
Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源
1. 在多个编译版本中,中英文在传递时,可能采用不同的格式
_UNICODE定义时,中英文字符都按unicode格式存储(CString使用CStringW-wchar_t型存储)
MBCS时,英文字符1个字节,中文字符两个字节(CString使用CStringA-char型存储)
参考下面来自msdn的说明:我们看到CString基于_UNICODE宏,存储数据char 或 w_char类型。
CString is based on the TCHAR data type. If the symbol _UNICODE is defined for your program, TCHAR is defined as type wchar_t, a 16-bit character type; otherwise, it is defined as char, the normal 8-bit character type. Under Unicode, then, CString objects are composed of 16-bit characters. Without Unicode, they are composed of 8-bit char type.
When not using _UNICODE, CString is enabled for multibyte character sets (MBCS, also known as double-byte character sets, DBCS).
再来看下:MBCS说明
最常见的 MBCS 实现是双字节字符集 (DBCS)。一般来说,Visual C++(尤其是 MFC)完全支持 DBCS
在 MBCS 下,字符被编码为单字节或双字节。在双字节字符中,第一个字节(即前导字节)表示它和下一个字节将被解释为一个字符。第一个字节来自留作前导字节的代码范围。哪个范围的字节可以用作前导字节取决于所使用的代码页。例如,日文代码页 932 使用 0x81 到 0x9F 范围内的字节作为前导字节,而朝鲜语代码页 949 则使用其他范围的字节。
2. 当我们涉及中英文存储交互的时候,需要同时支持两种情况(MBCS/UNICODE)
程序怎么写?
配置文件怎么写?
关于我个人当前有这两种思路
第一种:文件格式固定
例如:文件格式我们采用uncode存储;
读取时:程序本身在运行的时候,判断当前是unicode,还是mbcs,采用
sizeof(TCHAR) == 1 则为 mbcs
sizeof(TCHAR) == 2 则为 unicode
当为unicode时,则直接从文件中读取字符串即可,就能读到正确的中英文字符,这些读出的内容可以正确展现到界面中去
当程序以mbcs格式运行时,需要把unicode的字符串内容,通过转换,转换成mbcs,然后再展现到界面中去
写入时:我们一定要采用unicode写入,当系统为mbcs时,我们把数据转为unicode存储到wchar_t*的buffer中,然后写入。
第二种:记录字符串格式到文件中
例如:程序写入文件的时候,判断当前的环境,如果为mbcs,则记录1,如果为unicode,则记录0。
在读取的时候,先判断自身的运行环境mbcs/unicode,然后再读入文件中的信息mbcs/unicode;如果两者一致,则直接读入即可。
如果两者不一致,则需要转换,转换到对应的运行环境中的格式。
例如:环境为unicode,文件存储的是mbcs,则需要把文件中的字符串转为unicode,然后再展示
环境为mbcs,文件存储为unicode,则需要把文件中的字符床转为mbcs,然后展示到界面中
3. MBCS/UNICODE转换的方法
我们可以采用如http://blog.csdn.net/chunyexiyu/article/details/8938540中的方法转换(MultiByteToWideChar,WideCharToMultiByte)
这里介绍一个MFC中常用的方法
Unicode to ANSI:假如文件存储的类型是Unicode,环境的类型是MBCS
CString cstrName; // 环境为Unicode时,CString等同于CStringA,存储的数据为char类型
BYTE *pBuffer = new BYTE[dwStrNameLength];
binFile.Read(pBuffer, dwStrNameLength); // 从文件中读取出Unicode格式来名称
{
USER_CONVERSION;
cstrName = W2CT(LPCWSTR(pBuffer));
}
delete pBuffer;
ANSI to Unicode:假如文件存储的类型是ASNI,环境的类型是Unicode
CString cstrName; // 环境为Unicode时,CString等同于CStringW,存储的数据为wchar_t类型
BYTE *pBuffer = new BYTE[dwStrNameLength];
binFile.Read(pBuffer, dwStrNameLength); // 从文件中读取出来ANSI格式存储名称
{
USER_CONVERSION;
cstrName = A2CT(LPCSTR(pBuffer));
}
delete pBuffer;
W2CT,A2CT 参见MSDN中说明:
CSourceType2[C]DestinationType[EX]
where:
-
SourceType and DestinationType are described in the table below.
-
[C] is present when the destination type must be constant.
-
[EX] is present when the initial size of the buffer must be specified as a template argument.
SourceType/DestinationType
Description
A
ANSI character string.
W
Unicode character string.
T
Generic character string (equivalent to W when _UNICODE is defined, equivalent to A otherwise).
OLE
OLE character string (equivalent to W).
4. USER_CONVERSION/W2CT等使用注意事项
USES_CONVERSION的宏定义参考如下:
int _convert = 0; (_convert);
UINT _acp = ATL::_AtlGetConversionACP() /*CP_THREAD_ACP*/; (_acp);
LPCWSTR _lpw = NULL; (_lpw);
LPCSTR _lpa = NULL; (_lpa)
A2W宏定义形如:
(_lpa = lpa) == NULL) ? NULL : _convert = (lstrlenA(_lpa)+1);
(INT_MAX/2<_convert)? NULL : ATLA2WHELPER((LPWSTR) alloca(_convert*sizeof(WCHAR)), _lpa, _convert, _acp);
W2A宏定义形如:
((_lpw = lpw) == NULL) ? NULL : (_convert = (lstrlenW(_lpw)+1),
(_convert>INT_MAX/2) ? NULL : ATLW2AHELPER((LPSTR) alloca(_convert*sizeof(WCHAR)), _lpw, _convert*sizeof(WCHAR), _acp)
其中其内存申请使用的是:alloca
alloca的话从stack申请空间,比较方便,不用我们释放,函数结束,stack地址自然会回退。
不过我们知道stack空间通常是有限制的,例如2M,所以我们使用时,
1. 避免在一个函数中循环使用alloca---担心溢出了
2. 如果确实需要,要么使用别的,例如MultiByteToWideChar; 也或者这样把一次的处理放到函数中,然后循环再调用
CString InnProc(i) { USER_CONVERSION; ... return A2CT(xxx); }
void Proc() {for (...) { xxx = InnProc(i)};
//这样的话,每次执行完InnProc,stack地址就回归一次,就不回越积越多的溢出了
3. alloca出的内存,要返回时,请copy一下再返回,这个和临时变量道理一样. 上面的例子使用的CString把字符串copy了一份拿了出来。要不函数退出时,函数产生的stack数据原则上要清掉的。
关于alloca的详细解释可以参考这篇文档:http://blog.csdn.net/masefee/article/details/6835688