如何在 Windows 上将 UTF-8 字符串打印到 std::cout?

新手上路,请多包涵

我正在用 C++ 编写一个跨平台的应用程序。所有字符串在内部都是 UTF-8 编码的。考虑以下简化代码:

 #include <string>
#include <iostream>

int main() {
    std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
    std::cout << test;

    return 0;
}

在 Unix 系统上, std::cout 8 位字符串是 UTF-8 编码的,所以这段代码可以正常工作。

然而,在 Windows 上, std::cout 8 位字符串采用 Latin-1 或类似的非 Unicode 格式(取决于代码页)。这导致以下输出:

希腊语:╬▒╬▓╬│╬┤;德语:├£bergr├Â├ƒentr├ñger

我该怎么做才能使 std::cout 在 Windows 上将 8 位字符串解释为 UTF-8?

这是我尝试过的:

 #include <string>
#include <iostream>
#include <io.h>
#include <fcntl.h>

int main() {
    _setmode(_fileno(stdout), _O_U8TEXT);
    std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
    std::cout << test;

    return 0;
}

我希望 _setmode 能解决问题。但是,这会在调用 operator<< 的行中导致以下断言错误:

Microsoft Visual C++ 运行时库

调试断言失败!

程序:d:\visual studio 2015\Projects\utf8test\Debug\utf8test.exe 文件:minkernel\crts\ucrt\src\appcrt\stdio\fputc.cpp 行:47

表达式: ( (_Stream.is_string_backed()) || (fn = _fileno(_Stream.public_stream()), ((_textmode_safe(fn) == __crt_lowio_text_mode::ansi) && !_tm_unicode_safe(fn))))

有关您的程序如何导致断言失败的信息,请参阅有关断言的 Visual C++ 文档。

原文由 Daniel Wolf 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.6k
2 个回答

问题不在于 std::cout 而是 Windows 控制台。 Using C-stdio you will get the ü with fputs( "\xc3\xbc", stdout ); after setting the UTF-8 codepage (either using SetConsoleOutputCP or chcp ) and 在 cmd 的设置中设置 Unicode 支持字体(Consolas 应该 支持超过 2000 个字符,并且有注册表黑客可以向 cmd 添加更多功能强大的字体)。

如果您使用 putc('\xc3'); putc('\xbc'); 一个接一个地输出一个字节,您将得到双豆腐,因为控制台将它们分别解释为非法字符。这可能是 C++ 流所做的。

有关详细讨论,请参阅 Windows 控制台上的 UTF-8 输出

对于我自己的项目,我终于实现了 std::stringbuf 转换为 Windows-1252。我真的需要完整的 Unicode 输出,但是这对你没有帮助。

另一种方法是覆盖 cout 的 streambuf,使用 fputs 作为实际输出:

 #include <iostream>
#include <sstream>

#include <Windows.h>

class MBuf: public std::stringbuf {
public:
    int sync() {
        fputs( str().c_str(), stdout );
        str( "" );
        return 0;
    }
};

int main() {
    SetConsoleOutputCP( CP_UTF8 );
    setvbuf( stdout, nullptr, _IONBF, 0 );
    MBuf buf;
    std::cout.rdbuf( &buf );
    std::cout << u8"Greek: αβγδ\n" << std::flush;
}

我在这里关闭了输出缓冲,以防止它干扰未完成的 UTF-8 字节序列。

原文由 mkluwe 发布,翻译遵循 CC BY-SA 3.0 许可协议

自从我开始使用 {fmt} 库以来,我所有的编码问题都消失了。

一个简单的使用示例:

 #include <fmt/core.h>

int main() {
  fmt::print("Greek: αβγδ; German: Übergrößenträger\n");
}

原文由 woocom 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏