当前位置: 首页 > news >正文

关于 wcout 输出中文的问题

1. cout

场景1
: 在源文件中定义 const char* str = "中文" 在 VC++ 编译器上,由于Windows环境用 GBK编码,所以字符串 "中文" 被保存为 GBK内码,编译器也把 str 指向一个包含有 GBK编码的只读内存空间(因为是const)。用 cout 输出 str 时,由于中文Windows环境用GBK编码,所以把GBK编码的 str 内容输出到控制台,没问题。 

场景2: 在Linux 下编辑一个文件 const char* str = "中文", 由于Linux普遍使用 UTF8 编码,所以在源文件里, "中文" 被保存为 UTF8内码。然后在Windows中打开这个源文件,由于Windows使用GBK编码,所以VC++ 按照GBK去解释被保存为 UTF8 内码的 "中文", 显示为乱码。

2. wcout 

在源文件中定义 const wchar_t* str = L"中文" 在 VC++ 编译器上,由于指定了L,所以字符串 "中文" 被保存为UNICODE内码(UCS2),编译器也把 str 指向一个包含有 UNICODE 编码的只读内存空间。用 wcout 输出 str 时, wcout 首先调用 wcstombs() (即根据当前 local 转换, 如果没有设置local,则是经典的C local, 不认识中文)把 str 的内容转换后交给控制台,结果自然什么都不显示. (调试代码可以知道VC++ 2010 实现是一个字符一个字符输出,调用 wctomb_s) 

原理 
我们知道 cout 和 wcout 分别是 basic_ostream 的特化版本, 而 basic_ostream 调用 basic_streambuf 实际执行输出动作,针对 wchar_t,basic_streambuf有专门的特化函数,调用 fputwc 输出一个宽字符,而 fputwc 需要调用 wctomb_s 把宽字符转换后再输出。我们知道wctomb_s 是依赖 locale 的由于默认情况下是C locale,所以用中文内码调用 wctomb_s 会失败。

解决办法 
设置当前系统的locale 替代默认的 "C" locale, 使 wctomb_s 等函数可以正常工作。
以下3种方法中的任意一种都可以达到目的。

1. C函数设置全局locale 
setlocale(LC_ALL, ""); 

2. C++ 设置全局locale 
std::locale::global(std::locale("")); 

2. 单独为 wcout 设置一个 locale 
std::locale loc(""); 
std::wcout.imbue(loc); 

结论 
和Windows API 不同 C++中的各种 w版本的类或者函数并不能提高性能,因为它们都需要用 wc..to..mb 之类的函数转换为ANSI兼容编码然后调用标准库函数。
或者,如果库函数的实现者愿意,针对Windows系统,宽字符的fputwc可以直接调用UNICODE版本的Windows API而不用转换。但是这些都跟C++语言本身没有什么关系。 
由于Windows内核是UNICODE的,所以直接用 UNICODE 字符串调用 Windows API会有一点点好处。

C++设计者的出发点: 我不管你用什么字符编码,与C++无关,要输出时:如果是单字节字符或者多字节字符,直接输出如果是宽字符,则根据local转换为多字节字符,然后再输出
即使将来UNICODE过时了(假设,假设而已),也不要紧,只要定义好新的local即可。对于C语言也是这样。Windows设计者的出发点:统一使用 Unicode 宽字符,解决一切问题。

相关文章:

  • 非静态成员必须与特定对象相对
  • 压缩文件修复
  • 循环冗余校验(CRC)算法入门引导
  • C++ getline函数用法详解
  • cout后面输出时加endl和不加endl的区别
  • iostream
  • ChromeNativeMessaging 原生消息通信
  • IT前端开发和后端开发
  • socket原理及实例
  • 关于iostream与using namespace std 的解析
  • ws2_32.dll和wsock32.dll
  • lib文件
  • 阻塞和非阻塞
  • 并发、并行、串行
  • 什么是缓冲区(buffer),什么是缓存(cache)
  • php的引用
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • Elasticsearch 参考指南(升级前重新索引)
  • Hexo+码云+git快速搭建免费的静态Blog
  • If…else
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Linux链接文件
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • 编写高质量JavaScript代码之并发
  • 基于webpack 的 vue 多页架构
  • 力扣(LeetCode)965
  • 马上搞懂 GeoJSON
  • 区块链技术特点之去中心化特性
  • 我的面试准备过程--容器(更新中)
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • # Maven错误Error executing Maven
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (3)llvm ir转换过程
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (第27天)Oracle 数据泵转换分区表
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (转)jQuery 基础
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .Net的DataSet直接与SQL2005交互
  • .NET中使用Protobuffer 实现序列化和反序列化
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • @angular/cli项目构建--Dynamic.Form
  • @Data注解的作用
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [C/C++]_[初级]_[关于编译时出现有符号-无符号不匹配的警告-sizeof使用注意事项]