热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试

C标准为C标准IO库设计了十分完善的国际化文本处理机制。但在实际使用中,却发现各种编译器对它的支持性存在较大的差异,很多时候无法正确的输出字符。于是我对

 C++标准为C++标准IO库设计了十分完善的国际化文本处理机制。但在实际使用中,却发现各种编译器对它的支持性存在较大的差异,很多时候无法正确的输出字符。于是我对此进行了深入的调查。


一、说明

1.1 测试程序

  下面有一段很简单的程序,分别利用cout、wcout、printf输出字符串。具体代码为——

#include
#include

#include
#include <string>
#include
using namespace std;const char* psa &#61; "A汉字ABC";
const wchar_t* psw &#61; L"W汉字ABC";int main(int argc, char* argv[])
{
// init.//ios::sync_with_stdio(false); // Linux gcc.locale::global(locale(""));//setlocale(LC_CTYPE, ""); // MinGW gcc.wcout.imbue(locale(""));// C&#43;&#43;cout <endl;wcout <endl;// Cprintf("\nC:\n");printf("\t%s\n", psa);printf("\t%ls\n", psw);return 0;
}

 


  大家猜一猜这段程序的运行结果是什么&#xff1f;


1.2 理论结果

  先根据C&#43;&#43;标准&#xff0c;分析一下这段程序的理论结果。

  在main函数中&#xff0c;首先执行了这两行代码对地区环境进行了初始化——

locale::global(locale(""));wcout.imbue(locale(""));

 

  细节解释——
1. locale("")&#xff1a;调用构造函数创建一个local&#xff0c;其中的空字符串具有特殊含义&#xff1a;使用客户环境中缺省的locale&#xff08;《C&#43;&#43;标准程序库—自修教程与参考手册》P697&#xff09;。例如在简体中文系统上&#xff0c;会返回简体中文的locale。
2. locale::global(locale(""))&#xff1a;将“C&#43;&#43;标准IO库的全局locale”设为“客户环境中缺省的locale”。注意它还会设置C标准库的locale环境&#xff0c;造成与“setlocale(LC_ALL, "")”类似的效果&#xff08;《C&#43;&#43;标准程序库—自修教程与参考手册》P698&#xff09;。
3. wcout.imbue(locale(""))&#xff1a;使wcout使用“客户环境中缺省的locale”。

  就这样&#xff0c;使C标准库、C&#43;&#43;标准IO库&#xff08;尤其是wcout&#xff09;均正确的设置了地区环境&#xff0c;与客户环境中缺省环境完全匹配。

  随后&#xff0c;使用C&#43;&#43;标准IO库的cout、wcout分别输出窄字符串和宽字符串——

// C&#43;&#43;cout <endl;wcout <

 

  细节解释——
1. 调用cout、wcout的clear成员函数是为了清除错误状态&#xff0c;使后续输出能正常运行。
2. 使用“cout<

  最后&#xff0c;使用C标准库的printf函数输出窄字符串和宽字符串——

// Cprintf("\nC:\n");printf("\t%s\n", psa);printf("\t%ls\n", psw);

 

  所以&#xff0c;测试程序的运行结果应当为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  注意为了更好区分C&#43;&#43;标准IO库与C标准库的输出结果&#xff0c;这里给printf加了个TAB字符。


二、测试VC2005

  因VC2005是VC系列中第一个对C&#43;&#43;03标准支持性较好的编译器&#xff0c;先来测测它。


2.1 Debug版

  在VC2005中以Debug模式编译测试程序&#xff0c;执行结果为——

A
WC:A汉字ABCW汉字ABC

 

  可见C&#43;&#43;的cout、wcout均无法正常输出中文字符。
  而C的printf都能正常输出含中文字符的窄字符串与宽字符串。


2.2 Release版

  将编译配置改为“Release”模式&#xff0c;再编译运行&#xff0c;神奇的事情发生了。执行结果为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  Release版下全部通过&#xff0c;cout、wcout、printf均能正常输出。


三、测试VC2008及更高版本的VC

  在VC2008中编译测试程序&#xff0c;执行结果为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  全部通过&#xff0c;cout、wcout、printf均能正常输出。然后测试了Release版&#xff0c;也是全部通过。看来VC2005的Bug已经被修正了。
  随后又测试了VC2010、VC2012&#xff0c;均是全部通过。


四、测试Windows中的MinGW

4.1 测试

  使用GCC 4.6.2&#xff08;MinGW(20120426)&#xff09;编译测试程序&#xff0c;执行结果为——

A汉字ABC
WC:A汉字ABCW

 

  窄字符串都能正常输出&#xff0c;但宽字符串都不能正常输出。


4.2 修改代码&#xff0c;使MinGW能正常显示

  将初始化代码增加一行——

// init.locale::global(locale(""));setlocale(LC_CTYPE, ""); // MinGW gcc.wcout.imbue(locale(""));

 

  再用MinGW编译运行&#xff0c;执行结果为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  全部通过了&#xff0c;cout、wcout、printf均能正常输出。看来MinGW中的“locale::global(locale(""))”不会设置“setlocale(LC_ALL, "")”&#xff0c;必须手动调用。
  用VC2008编译刚才修改后的代码&#xff0c;也是全部通过。多调用一次“setlocale(LC_ALL, "")”并不会造成破坏。


五、测试Linux下的gcc

5.1 测试

  使用Linxu中的GCC编译测试程序&#xff0c;执行结果为——

A汉字ABC
WIWABCC:A汉字ABCW汉字ABC

 

  cout、printf均能正常输出&#xff0c;但wcout不能正常输出。


5.2 修改代码&#xff0c;使Linux下能正常显示

  将初始化代码增加一行——

// init.ios::sync_with_stdio(false); // Linux gcc.locale::global(locale(""));wcout.imbue(locale(""));

 

 

  再用gcc编译运行&#xff0c;执行结果为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  全部通过了&#xff0c;cout、wcout、printf均能正常输出。


5.3 第2次修改代码&#xff0c;使MinGW能正常显示

  切换回Windows&#xff0c;使用MinGW编译刚才修改后的代码&#xff0c;执行结果为——

A汉字ABCC:A汉字ABCW

 

  宽字符串又不能正常显示了。
  根据上次的经验&#xff0c;将初始化代码增加“setlocale”——

// init.ios::sync_with_stdio(false); // Linux gcc.locale::global(locale(""));setlocale(LC_CTYPE, ""); // MinGW gcc.wcout.imbue(locale(""));

 

  再用MinGW编译运行&#xff0c;执行结果为——

A汉字ABC
W汉字ABCC:A汉字ABCW汉字ABC

 

  终于全部通过了。


5.4 在Linux中测试第2次修改代码

  在Linux中测试第2次修改代码&#xff0c;全部通过。

  再用VC2008编译刚才修改后的代码&#xff0c;也是全部通过。

  看来终于找到VC、MinGW、Linux下均有效的初始化方法了。可惜“ios::sync_with_stdio(false)”禁用同步后需要手动进行同步&#xff0c;会造成某些旧代码工作不正常&#xff0c;该方法实用性不大。


六、测试Mac OSX下的gcc

  使用Linxu中的GCC编译测试程序&#xff0c;执行结果为——

  这么简单的程序&#xff0c;居然运行时报错了。这是什么原因呢&#xff1f;
  用gdb调试该程序。r运行&#xff0c;where显示调用栈&#xff0c;list显示源码——

  可以看出&#xff0c;是在执行“locale("")”时报错的。
  “locale("")”不是C&#43;&#43;标准中规定的吗&#xff0c;怎么连它都会报错&#xff1f;
  在网上搜索了一下&#xff0c;发现有人查过mac下的gcc源码&#xff0c;它在注释中明确写了"Currently, the generic model only supports the "C" locale."——
http://stackoverflow.com/questions/1745045/stdlocale-breakage-on-macos-10-6-with-lang-en-us-utf-8
std::locale breakage on MacOS 10.6 with LANG&#61;en_US.UTF-8


七、总结

  虽然C&#43;&#43;标准的设想十分完善&#xff0c;可惜各种编译器的实现程度存在不少差异。甚至某些平台上连“locale("")”都不支持。
  为了保证跨平台&#xff0c;慎用C&#43;&#43;标准IO库&#xff0c;最好尽可能的使用兼容性非常好的C标准库。

 

参考文献——
《ISO/IEC 9899:1999》&#xff08;C99&#xff09;. ISO/IEC&#xff0c;1999. www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
《C&#43;&#43; International Standard - ISO IEC 14882 Second edition 2003》&#xff08;C&#43;&#43;03&#xff09;. ISO/IEC&#xff0c;2003-10-15.
《C&#43;&#43;标准程序库—自修教程与参考手册》. Nicolai M.Josuttis 著&#xff0c;侯捷、孟岩 译. 华中科技大学出版社&#xff0c;2002-09.
《std::locale breakage on MacOS 10.6 with LANG&#61;en_US.UTF-8》. http://stackoverflow.com/questions/1745045/stdlocale-breakage-on-macos-10-6-with-lang-en-us-utf-8
《[C] 跨平台使用TCHAR——让Linux等平台也支持tchar.h&#xff0c;解决跨平台时的格式控制字符问题&#xff0c;多国语言的同时显示》. http://www.cnblogs.com/zyl910/archive/2013/01/17/tcharall.html

 


源码下载——
http://files.cnblogs.com/zyl910/wchar_crtbug.rar


作者&#xff1a;zyl910
出处&#xff1a;http://www.cnblogs.com/zyl910/
本文版权归作者所有&#xff0c;欢迎转载&#xff0c;但未经作者同意必须保留此段声明&#xff0c;且在文章页面明显位置给出原文连接&#xff0c;否则保留追究法律责任的权利.


推荐阅读
  • 字符串学习时间:1.5W(“W”周,下同)知识点checkliststrlen()函数的返回值是什么类型的?字 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 本文对常见的字符串哈希函数进行了全面分析,涵盖了BKDRHash、APHash、DJBHash、JSHash、RSHash、SDBMHash、PJWHash和ELFHash等多种算法。这些哈希函数在不同的应用场景中表现出各异的性能特点,通过对比其算法原理、计算效率和碰撞概率,为实际应用提供了有价值的参考。 ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • ### 优化后的摘要本文对 HDU ACM 1073 题目进行了详细解析,该题属于基础字符串处理范畴。通过分析题目要求,我们可以发现这是一道较为简单的题目。代码实现中使用了 C++ 语言,并定义了一个常量 `N` 用于字符串长度的限制。主要操作包括字符串的输入、处理和输出,具体步骤涉及字符数组的初始化和字符串的逆序操作。通过对该题目的深入探讨,读者可以更好地理解字符串处理的基本方法和技巧。 ... [详细]
  • MATLAB字典学习工具箱SPAMS:稀疏与字典学习的详细介绍、配置及应用实例
    SPAMS(Sparse Modeling Software)是一个强大的开源优化工具箱,专为解决多种稀疏估计问题而设计。该工具箱基于MATLAB,提供了丰富的算法和函数,适用于字典学习、信号处理和机器学习等领域。本文将详细介绍SPAMS的配置方法、核心功能及其在实际应用中的典型案例,帮助用户更好地理解和使用这一工具箱。 ... [详细]
  • 在C语言程序开发中,调试和错误分析是确保代码正确性和效率的关键步骤。本文通过一个简单的递归函数示例,详细介绍了如何编写和调试C语言程序。具体而言,我们将创建一个名为 `factorial.c` 的文件,实现计算阶乘的功能,并通过逐步调试来分析和解决可能出现的错误。此外,文章还探讨了常见的调试工具和技术,如GDB和断点设置,以帮助开发者高效地定位和修复问题。 ... [详细]
  • 本文详细介绍了在Linux系统上编译安装MySQL 5.5源码的步骤。首先,通过Yum安装必要的依赖软件包,如GCC、GCC-C++等,确保编译环境的完备。接着,下载并解压MySQL 5.5的源码包,配置编译选项,进行编译和安装。最后,完成安装后,进行基本的配置和启动测试,确保MySQL服务正常运行。 ... [详细]
  • 在Ubuntu系统中配置Python环境变量是确保项目顺利运行的关键步骤。本文介绍了如何将Windows上的Django项目迁移到Ubuntu,并解决因虚拟环境导致的模块缺失问题。通过详细的操作指南,帮助读者正确配置虚拟环境,确保所有第三方库都能被正确识别和使用。此外,还提供了一些实用的技巧,如如何检查环境变量配置是否正确,以及如何在多个虚拟环境之间切换。 ... [详细]
  • 开源实习机会 | Compiler SIG 正式发布实习任务,诚邀您加入申请!
    对编译技术充满兴趣却苦于无从入手?当前疫情形势下,外出实习变得困难重重?现在,Compiler SIG 正式发布了一系列实习任务,为有志之士提供了宝贵的机会。无论你是初学者还是有一定基础的学生,都能在这里找到适合自己的实践项目。我们诚挚邀请您的加入,共同探索编译技术的无限可能! ... [详细]
author-avatar
nnyoyo8
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有