C++ 11 std :: cout <<"UTF-8中的字符串文字"到Windows cmd控制台?(Visual Studio 2015)

pep*_*epr 5 windows cmd utf-8 c++11 visual-c++-2015

简介:如何cmd使用std::cout流将使用UTF-8编码(Windows CP 65001)存储的源代码中定义的字符串文字正确打印到控制台?

动机:我想修改优秀的Catch单元测试框架(作为实验),以便它显示带有重音字符的文本.修改应该简单,可靠,并且对其他语言和工作环境也应该有用,以便作者可以接受它作为增强.或者,如果你知道Catch,如果有其他替代解决方案,你可以发布吗?

细节:让我们从捷克版的"快速棕色狐狸......"开始吧

#include <iostream>
#include "windows.h"

using namespace std;

int main()
{
    cout << "\n-------------------------- default cmd encoding = 852 -------------------\n";
    cout << "P?íšern? žlu?ou?ký k?? úp?l ?ábelské ódy!" << endl;

    cout << "\n-------- Windows Central European (1250) set for the cmd console --------\n";
    SetConsoleOutputCP(1250);
    std::cout << "P?íšern? žlu?ou?ký k?? úp?l ?ábelské ódy!" << std::endl;

    cout << "\n------------- Windows UTF-8 (65001) set for the cmd console -------------\n";
    SetConsoleOutputCP(CP_UTF8);
    std::cout << "P?íšern? žlu?ou?ký k?? úp?l ?ábelské ódy!" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

它打印以下内容(字体设置为Lucida控制台): 在此输入图像描述

cmd默认编码是852,默认窗口编码是1250,和源代码使用65001编码(UTF-8与BOM)中的溶液保存.以SetConsoleOutputCP(1250);cmd编程方式相同的方式更改编码(以编程方式)chcp 1250.

观察:设置1250编码时,正确打印UTF-8字符串文字.我相信它可以解释,但它真的很奇怪.是否有任何体面的,人性的,一般的方法来解决问题?

更新:"narrow string literal"在我的情况(本机Windows编码中欧)使用Windows-1250编码存储.它似乎独立于源代码的编码.编译器将其保存在Windows本机编码中.因此,切换cmd到该编码可提供所需的输出.它是uggly,但是如何以编程方式获取本机Windows编码(将其传递给SetConsoleOutputCP(cpX))?我需要的是一个对编译发生的机器有效的常量.它不应该是可执行文件运行的机器的本机编码.

C++ 11也引入了u8"the UTF-8 string literal",但它似乎不适合SetConsoleOutputCP(CP_UTF8);

pep*_*epr 2

这是通过 luk32 跳转链接并确认 Melebius 评论找到的部分答案(见问题下方)。这不是完整的答案,我很乐意接受您的后续评论。

\n

我刚刚找到了触及这个问题的UTF-8 Everywhere Manifesto 。要点17. 问:如何在 C++ 代码中编写 UTF-8 字符串文字?说(对于 Microsoft C++ 编译器也是明确的):

\n
\n

然而,最直接的方法是按原样写入字符串并保存以 UTF-8 编码的源文件:

\n
                                "\xe2\x88\x83y \xe2\x88\x80x \xc2\xac(x \xe2\x89\xba y)"\n
Run Code Online (Sandbox Code Playgroud)\n

不幸的是,MSVC 将其转换为某些 ANSI 代码页,从而损坏了字符串。要解决此问题,请将文件保存为不带BOM 的 UTF-8。MSVC 将假定它位于正确的代码页中,并且不会触及您的字符串。但是,它使得无法使用 Unicode 标识符和宽字符串文字(无论如何您都不会使用它们)。

\n
\n

我真的很喜欢这个宣言。简而言之,使用粗鲁的语言,并且可能过于简单化,它说:

\n
\n

忽略wstringwchar_t、 之类的东西。忽略代码页。忽略字符串文字前缀,如L, u, U, u8。到处使用 UTF-8。写出所有文字"naturally"。确保它也存储在编译的二进制文件中。

\n
\n

如果下面的代码以UTF-8无BOM存储...

\n
#include <iomanip>\n#include <iostream>\n#include "windows.h"\n\nusing namespace std;\n\nint main()\n{\n    SetConsoleOutputCP(CP_UTF8);\n    cout << "P\xc5\x99\xc3\xad\xc5\xa1ern\xc4\x9b \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88 \xc3\xbap\xc4\x9bl \xc4\x8f\xc3\xa1belsk\xc3\xa9 \xc3\xb3dy!" << endl;\n\n    int cnt = 0;\n    for (unsigned int c : "P\xc5\x99\xc3\xad\xc5\xa1ern\xc4\x9b \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88 \xc3\xbap\xc4\x9bl \xc4\x8f\xc3\xa1belsk\xc3\xa9 \xc3\xb3dy!") \n    {\n        cout << hex << setw(2) << setfill(\'0\') << (c & 0xff);\n        ++cnt;\n        if (cnt % 16 == 0)      cout << endl;\n        else if (cnt % 8 == 0)  cout << " | ";\n        else if (cnt % 4 == 0)  cout << "  ";\n        else                    cout << \' \';\n    }\n    cout << endl;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

它打印(应该是 UTF-8 编码)...

\n

在此输入图像描述

\n

当将源文件保存为带有 BOM 的 UTF-8 时,它会打印不同的结果......

\n

在此输入图像描述

\n

然而,问题仍然存在——如何以编程方式设置控制台编码,以便正确打印 UTF-8 字符串。

\n

我放弃。控制台cmd已经损坏,不值得从外部修复它。我接受我自己的评论只是为了结束问题。如果有人找到与 Catch 单元测试框架相关的不错的解决方案(可能完全不同),我将很高兴接受他/她的评论作为答案。

\n