首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Windows上将UTF-8字符串打印到std::cout?

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

Stack Overflow用户
提问于 2017-08-08 18:45:55
回答 8查看 47.4K关注 0票数 38

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

代码语言:javascript
运行
复制
#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位字符串为拉丁文1或类似的非Unicode格式(取决于代码页)。这将导致以下输出:

希腊语:╬▒╬▓╬│╬┤;德语:├ger├├ƒentr├ger

如何使std::cout 在上将8位字符串解释为UTF-8?

这就是我试过的:

代码语言:javascript
运行
复制
#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<<的行中出现以下断言错误

MicrosoftVisualC++运行时库 调试断言失败! 程序: d:\visual studio 2015\Projects\utf8test\Debug\utf8test.exe文件:2015\Projects\utf8test\Debug\utf8test.exe行: 47 表达式:(_Stream.is_string_backed()) x= _fileno(_Stream.public_stream()),((_textmode_safe (fn ) == __crt_lowio_text_mode::ansi)和!_tm_unicode_safe(Fn) 有关程序如何导致断言失败的信息,请参阅“断言”上的VisualC++文档。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2017-08-09 10:44:13

问题不是std::cout,而是windows控制台。使用cmd,您将在设置UTF-8代码页(使用üchcp)和在cmd设置中设置支持字体的Unicode (Consolas应该支持2000多个字符,并且有注册表hacks可以向cmd添加更多的字体)之后,获得带有fputs( "\xc3\xbc", stdout );chcp

如果使用putc('\xc3'); putc('\xbc');输出一个字节又一个字节,那么当控制台将其单独解释为非法字符时,您将得到双豆腐。这可能就是C++流所做的。

有关宽大的讨论,请参见Windows控制台上的UTF-8输出

对于我自己的项目,我最终实现了一个std::stringbuf,它执行到Windows1252的转换。我真的需要完整的Unicode输出,但是这不会对你有帮助。

另一种方法是覆盖cout的streambuf,在实际输出中使用fputs

代码语言:javascript
运行
复制
#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字节序列。

票数 15
EN

Stack Overflow用户

发布于 2017-08-10 20:18:57

最后,我让它起作用了。这个答案结合了Miles Budnek,Paul和mkluwe的输入和我自己的一些研究。首先,让我从在Windows 10上运行的代码开始,然后,我将向您介绍这段代码,并解释为什么它不能在Windows 7上发挥作用。

代码语言:javascript
运行
复制
#include <string>
#include <iostream>
#include <Windows.h>
#include <cstdio>

int main() {
    // Set console code page to UTF-8 so console known how to interpret string data
    SetConsoleOutputCP(CP_UTF8);

    // Enable buffering to prevent VS from chopping up UTF-8 byte sequences
    setvbuf(stdout, nullptr, _IOFBF, 1000);

    std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
    std::cout << test << std::endl;
}

代码从设置代码页正如Miles Budnik所建议的开始。这将告诉控制台将接收到的字节流解释为UTF-8,而不是ANSI的某些变体。

接下来,Visual附带的STL代码中存在一个问题。std::cout将其数据打印到std::basic_filebuf类型的流缓冲区。当缓冲区接收到一个字符串(通过std::basic_streambuf::sputn())时,它不会将它作为一个整体传递给底层文件。相反,它将分别传递每个字节。正如mkluwe所解释的,如果控制台以单个字节的形式接收UTF-8字节序列,则不会将其解释为单个代码点。相反,它将把它们视为多个字符。UTF-8字节序列中的每一个字节本身都是一个无效的代码点,因此您将看到std::endl的代码点。这里有�,但它是通过设计关闭的。解决方法是为流启用缓冲。作为额外的好处,这将给您带来更好的性能。然而,您现在可能需要像我对std::endl那样定期刷新流,否则输出可能不会显示。

最后,Windows支持光栅字体和TrueType字体。正如保罗所指出的,光栅字体将简单地忽略控制台的代码页。因此,只有当控制台设置为TrueType字体时,非ASCII Unicode字符才能工作。在Windows 7之前,默认的字体是光栅字体,所以用户必须手动更改它。幸运的是,Windows 10将默认字体更改为Consolas。,所以问题的这一部分应该随着时间的推移自行解决。

票数 28
EN

Stack Overflow用户

发布于 2017-08-08 19:26:16

std::cout正在做它应该做的事情:它将您的UTF-8编码文本发送到控制台,但是您的控制台将使用它的当前代码页来解释这些字节。您需要将程序的控制台设置为UTF-8代码页:

代码语言:javascript
运行
复制
#include <string>
#include <iostream>
#include <Windows.h>

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

如果Windows将默认代码页切换到UTF-8,那就太好了,但由于向后兼容性的考虑,它们很可能无法实现。

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45575863

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档