补充 - C++字符串完全指引

转载 2007年09月23日 23:14:00

补充 - C++字符串完全指引.htm(转载:http://blog.csdn.net/ecai/archive/2004/12/07/207148.aspx)

写了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


 

C++字符串完全指引之一

点击打开链接 引言 毫无疑问,我们都看到过像 TCHAR, std::string, BSTR 等各种各样的字符串类型,还有那些以 _tcs 开头的奇怪的宏。你也许正在盯着显示器发愁。本指引...
  • gws1229
  • gws1229
  • 2014年12月11日 09:44
  • 316

C++字符串完全指引之二

点击打开链接 引言 因为C语言风格的字符串容易出错且不易管理,黑客们甚至利用可能存在的缓冲区溢出bug把C语言风格的字符串作为攻击目标,所以出现了很多字符串封装类。...
  • gws1229
  • gws1229
  • 2014年12月11日 09:46
  • 322

C++字符串完全指南

C++字符串完全指南 - Win32字符编码(一) 前言 字符串的表现形式各异,象TCHAR,std::string,BSTR等等,有时还会见到怪怪的用_tcs起头的宏。这个指...
  • a904003695
  • a904003695
  • 2016年04月17日 02:51
  • 857

C++字符串完全指引之二 —— 字符串封装类(转载)

C++字符串完全指引之二 —— 字符串封装类原著:Michael Dunn作者:Chengjie Sun 原文出处:CodeProject:The Complete Guide to C++ Stri...
  • binhualiu1983
  • binhualiu1983
  • 2016年06月12日 14:42
  • 165

C++字符串完全指引之一 —— Win32 字符编码 (转载)

C++字符串完全指引之一 —— Win32 字符编码原著:Michael Dunn翻译:Chengjie Sun 原文出处:CodeProject:The Complete Guide to C++ ...
  • binhualiu1983
  • binhualiu1983
  • 2016年06月12日 14:42
  • 92

C++字符串拷贝与缓冲区溢出

对于常用的字符串拷贝函数,常用的有: Ansi版本如下: strcpy, strncpy, strcpy_s, strncpy_s, StringCbCopy Unicode版本为:...
  • lc_910927
  • lc_910927
  • 2015年04月11日 16:23
  • 1070

Python 字符串格式化 (%操作符)

自适应辛普森公式辛普森公式是数值方法中常用的计算函数定积分的近似方法 计算定积分的方法 辛普森公式的推导 其他N-C公式 自适应方法 计算定积分的方法 求原函数 直接查表 这个求不出来怎么办∫x0t...
  • ghode_dave
  • ghode_dave
  • 2017年12月28日 16:18
  • 17

【C++】满二叉树与完全二叉树的区别及判断

满二叉树与完全二叉树的区别: (1)完全二叉树,除最后一层可能不满以外,其他各层都达到该层节点的最大数;最后一层如果不满,该层所有                               节点都...
  • ZDF0414
  • ZDF0414
  • 2015年11月29日 14:12
  • 1018

DIV居中之完全指引

每一个新手难免感到居中一个DIV不是如你所期望的容易。居中DIV里面的内容很容易,可以通过设置text-align属性为center, 但是事情往往会变得少许棘手. 当你去垂直居中一个DIV,你会感觉...
  • dc8899
  • dc8899
  • 2014年02月13日 16:54
  • 774

C++算法之 判断是否为完全二叉树

判断完全二叉树: 完全二叉树,除最后一层外,每一层上的节点树都达到了最大值;在最后一层上只缺少右边的若干节点! 算法思路: 按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,...
  • djb100316878
  • djb100316878
  • 2015年01月12日 11:36
  • 4787
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:补充 - C++字符串完全指引
举报原因:
原因补充:

(最多只允许输入30个字)