考虑以下代码片段,在MS Visual Studio 2010/2012上编译为控制台应用程序并在Win7上执行:
#include "stdafx.h"
#include <iostream>
#include <string>
const std::wstring test = L"hello\xf021test!";
int _tmain(int argc, _TCHAR* argv[])
{
std::wcout << test << std::endl;
std::wcout << L"This doesn't print either" << std::endl;
return 0;
}
第一个wcout语句输出“hello”(而不是“hello?test!”) 第二个wcout语句没有输出任何内容。
就好像0xf021(和其他?)Unicode字符会导致wcout失败。
这个特殊的Unicode字符0xf021(编码为UTF-16)是Basic Multilingual Plane中“Private Use Area”的一部分。我注意到Windows控制台应用程序没有对Unicode字符的广泛支持,但通常每个字符至少由默认字符(例如“?”)表示,即使不支持呈现特定字形。
导致wcout流阻塞的原因是什么?有没有办法在它进入这种状态后重置它?
答案 0 :(得分:13)
wcout
,或者确切地说,是内部使用的wfilebuf
实例,将宽字符转换为窄字符,然后将这些字符写入文件(在您的情况下,转移到stdout
) 。转换由流的区域设置中的codecvt
构面执行;默认情况下,它只是wctomb_s
,转换为系统默认的ANSI代码页,即CP_ACP
。
显然,在系统上配置的默认代码页中无法表示字符'\xf021'
。因此转换失败,并在流中设置failbit
。设置failbit
后,所有后续调用都会立即失败。
我不知道如何让wcout
成功地将任意Unicode字符打印到控制台。 wprintf
可以通过一些调整来实现:
#include <fcntl.h>
#include <io.h>
#include <string>
const std::wstring test = L"hello\xf021test!";
int _tmain(int argc, _TCHAR* argv[])
{
_setmode(_fileno(stdout), _O_U16TEXT);
wprintf(test.c_str());
return 0;
}
答案 1 :(得分:12)
将stdout的模式设置为_O_U16TEXT将允许您将Unicode字符写入wcout流以及wprintf。 (请参阅Conventional wisdom is retarded, aka What the @#%&* is _O_U16TEXT?)这是right way使其发挥作用。
_setmode(_fileno(stdout), _O_U16TEXT);
std::wcout << L"hello\xf021test!" << std::endl;
std::wcout << L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd" << std::endl;
std::wcout << L"Now this prints!" << std::endl;
它不再是必要的,但您可以通过调用clear来重置已进入错误状态的流:
if (std::wcout.fail())
{
std::wcout.clear();
}