写了n年程序,近来在字符串上栽了。:( 认真的研究了一些关于字符串的文章,在此记下。
		许多关于字符串的问题,在文章最后的参考文章中,相信有更加深入和精确的描述。不过关于中文的处理,我想先补充一些自己的看法。
		背景:WIN32 console程序,使用printf输出字符串。相信许多人都有使用过。
		平台:VisualStudio.NET 2003(MFC 7.1)。
		
				
						
								| 
 | MBCS | UNICODE | 
						
								| 蔡 | b2 cc | 21 85 | 
						
								| A | 41 00 | 41 00 | 
				
		
		
				程序段1:使用std::string
		
		#include <string>
{
// NOTE: use s1._Bx._Buf to see the memory
std::string s1 ("蔡"); // b2 cc 00
}
{
std::wstring s1(L"蔡"); // b2 00 cc 00 00 00
}
以上代码,不管使用MBCS还是_UNICODE编译,得到的结果都是一样的。因为string (实际上是basic_string)不会自动进行从MBCS到UNICODE的转换。所以使用printf或者wprintf输出即可。前提当然是你的系统需要支持中文。
		
				
让我们把代码修改一下,希望可以输出字符串的内容:
		{
std::string s1 ("蔡"); // b2 cc 00
OutputDebugStringA(s1.c_str());
printf(s1.c_str());
}
{
std::wstring s1(L"蔡"); // b2 00 cc 00 00 00
OutputDebugStringW(s1.c_str());
wprintf(s1.c_str());
}
OutputDebugString
实际上就是ATLTRACE()最后调用的函数,该函数向VisualStudio的Output窗口输出,而printf和wprintf向
console窗口输出。最后的结果如何?OutputDebugStringW输出的是怪字符!!WHY??
s1.c_str()传递给OutputDebugStringW和wprintf不是都是内容相同的LPCWSTR吗?
		1)因为OutputDebugStringW的字符串必须是真正UNICODE编码的字符串,而不是所有的const wchar_t*(即LPCWSTR)都可以得到正确结果。在这里s1虽然使用wchar_t类型,但是实际的内容却是MBCS编码。
		2)反之亦然:CRT的wprintf只支持MBCS编码的字符串,而不能是UNICODE编码的字符串。在程序段2我们可以看到真正的UNICODE编码的字符串。
		这是我多年来的一个误区:wchar_t类型的字符串就是UNICODE字符串。实际应该理解为UNICODE是16位的字符集,可以使用wchar_t类型进行存储。
		
				
				
				程序段2:使用CString
				
请看程序后面的说明。
		
				1.         ////////////////// START: compile with _UNICODE ///////////////////
		
		
				2.         {
		
		
				3.         CString s1 ("A"); // 41 00 00 00
		
		
				4.         }
		
		
				5.         {
		
		
				6.         CString s1 (L"A"); // 41 00 00 00
		
		
				7.         }
		
		
				8.         {
		
		
				9.         CString s1 (_T("A")); // 41 00 00 00
		
		
				10.     }
		
		
				11.     {
		
		
				12.     CString s1 ("
				蔡
				"); // 21 85 00 00
		
		
				13.     }
		
		
				14.     {
		
		
				15.     CString s1 (L"
				蔡
				"); // b2 00 cc 00 00 00
		
		
				16.     }
		
		
				17.     {
		
		
				18.     CString s1 (_T("
				蔡
				")); // b2 00 cc 00 00 00
		
		
				19.     }
		
		
				20.     ////////////////// END: compile with _UNICODE ///////////////////
		
		
				21.     ////////////////// START: compile with _MBCS ///////////////////
		
		
				22.     {
		
		
				23.     CString s1 ("A"); // 41 00
		
		
				24.     }
		
		
				25.     {
		
		
				26.     CString s1 (L"A"); // 41 00
		
		
				27.     }
		
		
				28.     {
		
		
				29.     CString s1 (_T("A")); // 41 00
		
		
				30.     }
		
		
				31.     {
		
		
				32.     CString s1 ("
				蔡
				"); // b2 cc 00
		
		
				33.     }
		
		
				34.     {
		
		
				35.     CString s1 (L"
				蔡
				"); // 32 a8 ac 00
		
		
				36.     }
		
		
				37.     {
		
		
				38.     CString s1 (_T("
				蔡
				")); // b2 cc 00
		
		
				39.     }
		
		
				40.     ////////////////// END: compile with _MBCS ///////////////////
		
		
				
1)对于英文字母‘A’,MBCS和UNICODE的结果都是一样的
		
				2)Line 15.     
		
		
				CString s1 (L"
				蔡
				"); // b2 00 cc 00 00 00
		
		
				得到的还是MBCS的字符串,只是增加了0作为trail byte。而不是我理解的UNICODE字符串!这是我多年来的另外一个误区:_T在_UNICODE下转换为L,而L后面的字符串是UNICODE编码。在参考资料MSDN的“TCHAR.H 中的一般文本映射”中(以及MSDN的许多地方),可以看到类似的说明:
		
				一般文本数据类型映射
		
		
				
						
								
										| 一般文本数据类型名 | 未定义 _UNICODE 或 _MBCS | 已定义 _MBCS
 | 已定义 _UNICODE 
 | 
								
										| _TCHAR | char | char | wchar_t | 
								
										| _TINT | int | int | wint_t | 
								
										| _TSCHAR | signed char | signed char | wchar_t | 
								
										| _TUCHAR | unsigned char | unsigned char | wchar_t | 
								
										| _TXCHAR | char | unsigned char | wchar_t | 
								
										| _T 或 _TEXT | 无效(由预处理器移除) | 无效(由预处理器移除) | L
												
												(将后面的字符或字符串转换成相应的 Unicode 形式) | 
						
				
		 
		实际上L"xxx"只是通知编译器,我们需要的是wchar_t类型的字符串,而不能影响编码。
		真正的UNICODE字符串在哪里?
		3)Line 12:
		等同于    CStringW s1 ("蔡"); // 21 85 00 00
		我们看到,得到了真正的UNICODE 字符串。因为CString(在MFC
7.1中,不存在MFC的CString,实际上由ATL::CStringT通过typedef定义而得)的构造函数,在这里实际上是CStringW
的构造函数,根据输入的参数是char类型字符串,会自动调用MultiByteToWideChar转换MBCS字符串为UNICODE字符串。
		
				4)相应Line 12,那么Line 35得到的结果32 a8 ac 00是什么?和Line 12类似:
		
		CString构造函数,在这里实际上是CStringA的构造函数,根据输入的参数是wchar_t类型字符串,会自动调用WideCharToMultiByte转换UNICODE字符串为MBCS字符串。但是根据2),我们知道,输入的参数不是UNICODE字符串,只是MBCS的wchar_t类型字符串,所以得到的是错误的编码。
		 
		总结以上,可知:
		1)CRT不能生成和处理UNICODE类型字符串,对于wchar_t类型字符串,只能处理MBCS编码;
		2)VC RunTime中带W后缀的函数,和所有的COM函数,对于wchar_t类型字符串,只能处理UNICODE编码;
		3)如果不考虑输出,只是进行拷贝、比较等操作,只要注意_T的含义和字符串的字符类型长度就可以了;但是如果需要输出,必须注意字符串的编码转换。
4)
使用UNICODE或者MBCS的编译选项,只是影响字符串的字符类型(自动识别_T,CString等,自动把函数转换为xxxxA()或者xxxxW
()),不影响字符串的编码。下表的代码结果不受编译选项影响(这也是编译器处理_T,CString等后的结果):
		
				
						
								| CStringA s = "xxx"; // 等于 CA2A("xxx") 结果为SBCS(单字节编码)
 printf()正确
 OutputDebugStringA()正确
 | CStringW s = "xxx"; // 等于 CA2W("xxx") 结果为UNICODE编码
 wprintf()错误
 OutputDebugStringW()正确
 | 
						
								| CStringA s = L"xxx"; // 等于 CW2A(L"xxx") 结果为MBCS编码(可能错误)
 printf()错误
 OutputDebugStringA()错误
 | CStringW s = L"xxx"; // 等于 CW2W("xxx") 不改变字符串的编码,仍然是MBCS。
 wprintf()正确
 OutputDebugStringW()错误
 | 
				
		
		
				疑问:我认为对于CW2W是由系统编码决定,可以直接得到UNICODE吗?
		
		关于CW2A,如果后面的字符串的确是UNICODE编码,则可以得到正确的相应MBCS编码字符串。实际上,这也是我们要输出UNICODE的方法:
		CStringW s = "蔡"; // s 现在是UNICODE编码
		// wprintf(s)不正确
		CW2A psz(s); // psz现在是s相应的正确的MBCS编码!
		printf(psz); // 正确
		// All is OK, a little more to say
		CA2W wsz(psz); // wsz现在是psz的错误的UNICODE编码,即32 a8 ac 00
		 
		 
		
				
						
								
										推荐参考资料
								
						
				
		
		
				- 
						The Complete Guide to C++ Strings:个人认为很好和很全面的文章
				
The Complete Guide to C++ Strings, Part I - Win32 Character Encodings
		
				http://www.codeproject.com/string/CPPStringGuide1.asp
		
		
				
The Complete Guide to C++ Strings, Part I - Win32 Character Encodings
http://www.codeproject.com/string/cppstringguide2.asp
		
				- 
						这2篇是不错的中文翻译。 :)
 
 
 C++字符串完全指引之一 —— Win32 字符编码
 http://www.vckbase.com/document/viewdoc/?id=1082
 
 C++字符串完全指引之二 —— 字符串封装类
 
 http://www.vckbase.com/document/viewdoc/?id=1096
 
 
 
- 
						其它一篇
 STL 字符串类与 UNICODE
 
 http://www.vckbase.com/vckbase/default.aspx
 
 
- 
						当然,少不了MSDN
 
 TCHAR.H 中的一般文本映射
 
 http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vccore/html/_core_generic.2d.text_mappings_in_tchar..h.asp
 
 建议:最好把整个“国际编程”目录看一次(虽然看完还是糊涂 :) )
 
 http://msdn.microsoft.com/library/CHS/vccore/html/_core_International_Programming_Topics.asp