wcout和cout混用的研究——sync_with_stdio
acmilan2017/04/26软件综合 IP:四川

C++字符有四个类型,char是1字节,wchar_t可能是2字节或4字节,char16_t和char32_t顾名思义。char和wchar_t是C++98就有的,支持也最好。char16_t和char32_t是C++11加入的,直到现在也没有很好的支持。

由于emoji这种东西的出现,支持Windows-1252/GBK/Big5已经不够用了,要支持Unicode。但是Windows中char编码是定死的,不支持Unicode。要想支持Unicode,不是说其它方法不行,而是使用wchar_t作为字符类型是最方便的做法。

iostream宽字符流对象,虽然在Windows下不支持Unicode,但是它们是写作wchar_t程序的必备武器,这是因为字符输入输出是最简单常用的程序交互形式,它们可进行编码自动转换,解决了wchar_t的交互问题,并且所有平台都有提供,不需要学习平台相关的API。

但是iostream库在Linux下有一个坑,默认情况下wcout和cout不能混用。这是因为受到Linux下的C语言stdio.h库的掣肘:

  • Windows下printf和wprintf可以混用,fwide函数是空函数。
  • Linux下printf和wprintf不可以混用,流的宽窄取决于首次调用哪个函数或先调用fwide(stream, 1)还是fwide(stream, -1),一旦确定即不可更改,除非重新打开。
  • 但是另一方面,Visual C++直到2015才支持重新打开相同文件的操作,之前一直不行,并且重新打开相同文件也是非常低效的做法。

不过实际上,只要提前调用了ios::sync_with_stdio(false),wcout和cout便不会通过stdio.h进行输入输出,而是自己管理缓冲区,就避免了wcout和cout不能混用的问题。

#include <iostream>
#include <locale>

using namespace std;

int main()
{
	// 不使用stdio.h进行输入输出,避免Linux/macOS下libc的宽窄流限制
	ios::sync_with_stdio(false);
	// 设置locale
	locale::global(locale(""));
	cout.imbue(locale());
	wcout.imbue(locale());
	// 现在可以混用cout和wcout了
	cout << "Hello, 窄 world!" << endl;
	wcout << L"Hello, 宽 world!" << endl;
	return 0;
}
</locale></iostream>

如果只更改编码,不更改区域格式的话:

locale::global(locale(locale("C"), new codecvt_byname<wchar_t, char, mbstate_t>("")));
</wchar_t,>

实际测试中,Ubuntu 16.04下的GCC和Windows下的Visual C++ 2017、Visual C++ 2008、Mac OS X 10.11下的Clang均通过了测试,正确输出了两个字符串。

一些非常老的Mac OS X可能会报locale("")错误,但是现在所有新版Mac OS X或macOS都是用的Clang编译器,已完全不存在这个问题。

Visual C++ 2005比较特殊。这个版本的iostream本身就有bug,只要设置locale以后cout和wcout就都没办法输出中文字符串了,不过只对控制台是有bug的,对文件没有。因此如果要兼容这个版本的话,最好配合ReadConsole/WriteConsole使用。不过,大部分情况下,我们都可以改用Visual C++ 2008来解决这个问题。

Ubuntu 16.04下的GCC输出结果(Mac OS X 10.11下的Clang差不多,从略):

pc@DESKTOP-PNUCILD:~$ g++ abc.cpp
pc@DESKTOP-PNUCILD:~$ ./a.out
Hello, 窄 world!
Hello, 宽 world!
pc@DESKTOP-PNUCILD:~$

Windows 10 Build 15063下Visual C++ 2017输出结果:

D:\>cl /EHsc abc.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

abc.cpp
Microsoft (R) Incremental Linker Version 14.10.25019.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:abc.exe
abc.obj

D:\>abc
Hello, 窄 world!
Hello, 宽 world!

D:\>

Windows 10 Build 15063下Visual C++ 2008输出结果:

D:\>cl /EHsc abc.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

abc.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:abc.exe
abc.obj

D:\>abc
Hello, 窄 world!
Hello, 宽 world!

D:\>

Windows 10 Build 15063下Visual C++ 2005输出结果:

D:\>cl /EHsc abc.cpp
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 14.00.50727.42 版
版权所有(C) Microsoft Corporation。保留所有权利。

abc.cpp
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:abc.exe
abc.obj

D:\>abc
Hello, Hello,
D:\>abc > abc.txt

D:\>type abc.txt
Hello, 窄 world!
Hello, 宽 world!

D:\>

[修改于 7年7个月前 - 2017/06/13 01:01:55]

来自:计算机科学 / 软件综合
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年11个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}