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

Project 编制进度计划【项目管理】 Project 项目管理编制进度计划详解。 阅读详情

写了n年程序,近来在字符串上栽了。:( 认真的研究了一些关于字符串的文章,在此记下。

许多关于字符串的问题,在文章最后的参考文章中,相信有更加深入和精确的描述。不过关于中文的处理,我想先补充一些自己的看法。

背景:WIN32 console程序,使用printf输出字符串。相信许多人都有使用过。

平台:VisualStudio.NET 2003(MFC 7.1)。

MBCSUNICODE
蔡b2 cc21 85
A41 0041 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
_TCHARcharcharwchar_t
_TINTintintwint_t
_TSCHARsigned charsigned charwchar_t
_TUCHARunsigned charunsigned charwchar_t
_TXCHARcharunsigned charwchar_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
华为OD机试真题 新系统 - 计算数列位置N的值 (Java/Py/C/C++/Js/Go) 本文整理华为 OD 机试新系统机考真题库,涵盖高频考点精析与完整题目内容,提供 C 语言、Java、C++、Python、Go、JavaScript 多语言源码实现,搭配详细解题思路讲解,并支持在线 OJ 刷题练习,助力高效备考华为 OD 机试新系统。 阅读详情

相关推荐

卷积码和Turbo码

通信原理相关博文目录:目录我们常在移动通信中遇到的卷积码就是一种非分组码,卷积码和信号处理中的卷积运算有关系吗?是不是就是信号处理中的卷积运算,先看看编码器的编码原理再说:下面是一个比较实用的卷积码编码器:它有三个移位寄存器D0,D1,D2和三个模2加法器,以及一个旋转开关组成,编码前,先将各级移位寄存器清零:现在假设输入的信息码元是1101:当输入的第一个码元为1时,三个模2加法器计算的结果都为...

Reborn Lee 2万+

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

  写了n年程序,近来在字符串上栽了。:( 认真的研究了一些关于字符串的文章,在此记下。许多关于字符串的问题,在文章最后的参考文章中,相信有更加深入和精确的描述。不过关于中文的处理,我想先补充一些自己的看法。背景:WIN32 console程序,使用printf输出字符串。相信许多人都有使用过。平台:VisualStudio.NET 2003(MFC 7.1)。

ecai的专栏 3739

Java面试题大全(2025版)

== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

2401_89221704的博客 2442

C++: VC6转VC8出现问题:error C2664: “fopen”: 不能将参数 1 从“CString”转换为“const char *”...

其实还可能出现其它类似的问题,如: error C2664: “fopen”: 不能将参数 1 从“CString”转换为“const char *” error C2039: “strcpy”: 不是“ATL::CStringT<BaseType,StringTraits>”的成员 但是程序在VC6下编译得很好,经过研究发现是设置上有所不同。在VC6中,默认使用MBC...

weixin_30414245的博客 461

VS2019手动添加bits/stdc++.h全指南(附解决‘无法打开源文件‘错误)

本文详细介绍了在Visual Studio 2019中手动配置C++万能头文件bits/stdc++.h的完整方法,解决常见的'无法打开源文件'错误。通过分析GCC与MSVC编译器生态差异,提供了纯手动创建和MinGW移植两种可靠方案,并给出详细的步骤、常见错误排查及最佳实践建议,帮助开发者高效配置竞赛开发环境。

weixin_29011395的博客 545

手把手教你用Apple Developer App完成苹果开发者账号注册(含证件验证失败解决方案)

本文详细指导如何通过Apple Developer App完成苹果开发者账号注册,特别针对证件验证失败问题提供解决方案。从准备工作到注册流程,再到验证失败的处理方法,帮助开发者顺利完成注册,避免常见错误。

moss5的博客 1156

c语言编译出现无效预处理器,C语言的编译期行为(二) #error预处理器指令

#error预处理器指令格式:#error any message该预处理将产生一条编译时错误信息,信息中包含#error后指定的内容。C语言标准中,要求对于#error后面的指定的内容也进行宏展开,但在我测试后发现Visual Studio 2008并没有对这一特性提供支持。所以,在我们的程序中,应尽量避免出现不同编译器实现可能各不相同的标准特性。该预处理器指令看似无用,因为一旦遇到它,程序的编...

weixin_29187895的博客 875

学术规范与论文写作(雨课堂)研究生 全部答案

1:如何造就优秀的学术论文 论文语法判断题 :全部是错的 2:如何撰写学术论文 3: SCI论文写作经验分享 4:论文写作与投稿技巧 5:学术伦理道德 答案仅供参考,研究生生活不易,大家如果有时间的话,还是可以学习学习的,毕竟老师讲的不错。加油,希望读研的每一个人都能顺利毕业! ...

Always-Learning 13万+

c++ 之字符串补充篇

string篇之补充 .c_str() 与 .data() 简述: c++17之前: 通过.c_str()方法可得到一个const char的指针指向str储存字符的内存空间 通过.data()方法可得到一个const char的指针指向str储存字符的内存空间 c++17之后: 通过.c_str()方法可得到一个const char的指针指向str储存字符的内存空间 注意: 通过.data()方...

航行的土豆的博客 1719

SAP SM30数据维护实战:条件筛选与动态校验的实现技巧

本文详细介绍了SAP SM30数据维护的实战技巧,重点讲解如何通过条件筛选实现精准数据查取,以及利用表维护生成器事件实现动态数据检查与校验。文章提供了从基础视图创建到高级事件编程的完整流程,包括调用函数VIEW_MAINTENANCE_CALL传递筛选条件,以及在NEW_DATA和CHANGE_SAVE事件中编写业务规则校验逻辑,帮助开发者提升SM30的管控能力与数据质量。

q9w8e7r6t5的博客 521

7.工具高级参数—CogPMAlignTool

CogPMAlign工具是用于图像模式匹配的视觉检测工具,主要包含训练参数和运行参数两类设置。训练参数包括弹性参数(处理图像非线性变形,建议不超过8像素)和特征粒度限制(控制边缘点密度)。运行参数涉及对比度阈值(影响特征点识别)、自动边缘阈值、接受阈值(0-1.0分值判定匹配有效性)和XY重叠(过滤重叠结果)等关键设置。该工具通过PatMax/PatQuick算法实现模板匹配,匹配分数0-1.0反映匹配度,受模板形状匹配度、自由度、特征缺失等因素影响。通过合理配置参数,可在执行速度和匹配精度间取得平衡,最高

934

c++ 不足位数补足某个字符的方法

#include<iostream> #include <iomanip> using namespace std; int main(){ int a; cin>>a; cout<<setfill('0')<<setw(4)<<a<<endl; return 0; } 可以看到输出结果如图所示

weixin_43757333的博客 2487

K8s(十二):监控与报警(163邮箱+钉钉)-Prometheus + Grafana + Alertmanager(超详细)

🔴 K8s(十二):监控与报警(163邮箱+钉钉)-Prometheus + Grafana + Alertmanager(超详细)

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 3万+

C++字符串完全指引一

原文出处:CodeProject:The Complete Guide to C++ Strings, Part I2005089632引言  毫无疑问,我们都看到过像 TCHAR, std::string, BSTR 等各种各样的字符串类型,还有那些以 _tcs 开头的奇怪的宏。你也许正在盯着显示器发愁。本指引将总结引进各种字符类型的目的,展示一些简单的用法,并告诉您在必要时,如何实现各

eulb的专栏 1233

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

C++字符串完全指引之二 —— 字符串封装类

C++字符串完全指引之二 —— 字符串封装类原著:Michael Dunn作者:Chengjie Sun原文出处:CodeProject:The Complete Guide to

Leo 1410

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++字符串完全指引之一 —— Win32 字符编码

 C++字符串完全指引之一 —— Win32 字符编码 原著:Michael Dunn 翻译:Chengjie Sun 原文出处:CodeProject:The Complete Guide to C++ Strings, Part I

CSYNYK的专栏 3730

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

C++字符串完全指引[转]

由Michael Dunn写的"The Complete Guide to C++ Strings"系列,两部分,中文由Chenjie Sun翻译。转载于VC知识库。第一部分: C++字符串完全指引之一 —— Win32 字符编码转载链接: http://www.vckbase.com/document/viewdoc/?id=1082第二部分: C++字符串完全指引之二 —— 字符串

Denlee的一片天 1874

C++字符串完全指引二

引言  因为C语言风格的字符串容易出错且不易管理,黑客们甚至利用可能存在的缓冲区溢出bug把C语言风格的字符串作为攻击目标,所以出现了很多字符串封装类。不幸的是,在某些场合下我们不知道该使用哪个字符串类,也不知道怎样把一个C风格的字符串转换成一个字符串封装类。  这篇文章将介绍所有在Win32 API, MFC, STL, WTL 和 Visual C++ 运行库中出现的字符串类型。我将描

青青陵上柏,磊磊涧中石 1094

C++字符串完全指引之一 —Win32 字符编码

C++字符串完全指引之一 —— Win32 字符编码原著:Michael Dunn翻译:Chengjie Sun原文出处:CodeProject:The Complete Guide to C++ Strings, Part I 引言  毫无疑问,我们都看到过像 TCHAR, std::string, BSTR 等各种各样的字符串类型,还有那些以 _t

Chenxinyi's Weblog 3894
上一篇: 详谈调用winpcap驱动写arp多功能工具
下一篇: 不要以为装了防火墙,我就监视不到你.(zt)
evers
博客等级 码龄24年 7粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值