为什么某些Unicode字符导致std :: wcout在控制台应用程序中失败?

时间:2013-10-05 02:45:15

标签: visual-c++ unicode

考虑以下代码片段,在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流阻塞的原因是什么?有没有办法在它进入这种状态后重置它?

2 个答案:

答案 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();
}