16

考虑以下代码片段,在 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)是基本多语言平面中“私人使用区”的一部分。我注意到 Windows 控制台应用程序没有广泛支持 Unicode 字符,但通常每个字符至少由默认字符表示(例如“?”),即使不支持呈现特定字形。

是什么导致 wcout 流阻塞?进入这种状态后有没有办法重置它?

4

2 回答 2

18

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;
}
于 2013-10-05T03:39:27.777 回答
14

将 stdout 的模式设置为 _O_U16TEXT 将允许您将 Unicode 字符写入 wcout 流以及 wprintf。(见传统智慧是迟钝的,又名 What the @#%&* is _O_U16TEXT?)这是完成这项工作的正确方法

_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();
}
于 2013-10-08T21:12:06.240 回答