C++ 尽管存在Windows或MinGW错误,但仍能体面地显示标准输出的大UTF-8编码字符串

C++ 尽管存在Windows或MinGW错误,但仍能体面地显示标准输出的大UTF-8编码字符串,c++,utf-8,output,c++17,mingw-w64,C++,Utf 8,Output,C++17,Mingw W64,第二次更新:我发现了一个非常简单的问题,其实不是那么难的问题,只是问了一天后。但人们似乎心胸狭窄,因此已经有三张接近票数的选票: “如何在Windows命令行中使用unicode字符?”(1x)的副本: 显然不是,这已在评论中得到澄清。这与我不使用的Windows命令行工具无关 不清楚你在问什么(1x): 那你一定要忍受痛苦。例如,当我问“是否有一种简单的方法可以确定std::string中的字符是否是UTF-8符号的非结束部分?”时,我再具体不过了(实际上,标记为粗体以提高可见性),并声明这足

第二次更新:我发现了一个非常简单的问题,其实不是那么难的问题,只是问了一天后。但人们似乎心胸狭窄,因此已经有三张接近票数的选票:

  • “如何在Windows命令行中使用unicode字符?”(1x)的副本:

    显然不是,这已在评论中得到澄清。这与我不使用的Windows命令行工具无关

  • 不清楚你在问什么(1x):

    那你一定要忍受痛苦。例如,当我问“是否有一种简单的方法可以确定std::string中的字符是否是UTF-8符号的非结束部分?”时,我再具体不过了(实际上,标记为粗体以提高可见性),并声明这足以回答问题(甚至解释原因)。说真的,甚至有图片显示了这个问题。此外,我自己现有的答案应该更清楚地说明这一点。你自己的缺陷不足以宣布某事太难理解

  • 过于宽泛(1x)(“请编辑问题,将其限制在具有足够详细信息的特定问题,以确定适当答案[…]):

    这肯定是另一个功能性肛门麻痹的问题。我明确表示,用一种方法解决这个问题(我已经找到了)就足够了。你可以找到一个适当的答案如下:看看我自己的答案。或者,如果你有能力的话,用你的大脑来解释我定义明确的单词,不幸的是,这个平台上的一些人似乎没有这样做

  • 然而,结束这个问题有一个实际的原因:它已经被解决了。但没有这样一种接近投票的选择。因此,很明显,Stack Exchange支持找到替代解决方案。因为我是一个好奇的人,我也对解决这个问题的其他方法感兴趣。如果你的智力不足,无法很好地理解问题所在,并且它在某些环境下是相当相关的(例如,使用Windows、C++在Eclipse CDT、UTF-8中,但<>强>没有<强> VisualStudio和<强> NO>强> Windows控制台,然后你就可以离开了,不必站在其他人的路上来满足他们的好奇心。谢谢

    第一次更新:我使用了
    app.exe>out.txt 2>&1
    生成的文件没有这些格式问题。所以问题是通常 std::cout执行此拆分,但底层控件 接收字符序列)必须处理正确的重新组装? (不幸的是,在Windows上似乎没有任何东西可以处理它,除了文件 溪流。所以我仍然需要绕过这一点。最好没有 首先写入文件并显示其内容——当然 工作。)

    在我使用的系统(Windows 7;MinGW-w64(GCC 8.1 for Windows))上,有一个
    std::cout
    的错误,因此UTF-8编码的字符串在重新组装之前就被打印出来,即使它们是通过传递一个大字符串在内部被
    std::cout
    反汇编的。下面的代码解释了bug的行为。但是,请注意,故障显示似乎是随机的,即
    std::cout
    切片(相等)
    std::string
    对象的方式对于程序的每次执行都是不等效的。但问题总是出现在1024倍的指数上,这就是我得出的结论

    #include <iostream>
    #include <sstream>
    
    void myFaultyOutput();
    void simulatedFaultyBehavior();
    
    int main()
    {
        myFaultyOutput();
        //simulatedFaultyBehavior();
    }
    
    void myFaultyOutput() {
        std::stringstream ss; // Note that ss is built correctly (which could be shown by saving ss.str() to a file).
        ss << "...";
        for (int i = 0; i < 20; i++) {
            for (int j = 0; j < 341; j++)
                ss << u8"\u301A";
            ss << "\n..";
        }
        std::cout << ss.str() << std::endl; // Problem occurs here, with cout.
        // Note that converting ss.str() to UTF-16 std::wstring and using std::wcout results in std::wcout not
        // displaying anything, not even ASCII characters in the future (until restarting the application).
    }
    
    // To display the problem on well-behaved systems ; just imagine the output would not contain newlines, while the faulty formatted characters remain.
    void simulatedFaultyBehavior() {
        std::stringstream ss;
        int amount = 2000;
        for (int j = 0; j < amount; j++)
            ss << u8"\u301A";
        std::string s = ss.str();
        std::cout << "s.length(): " << s.length() << std::endl; // amount * 3
        while (s.length() > 1024) {
            std::cout << s.substr(0, 1024) << std::endl;
            s = s.substr(1024);
        }
        std::cout << s << std::endl;
    }
    
    #包括
    #包括
    void myFaultyOutput();
    void simulatedFaultyBehavior();
    int main()
    {
    myFaultyOutput();
    //simulatedFaultyBehavior();
    }
    void myFaultyOutput(){
    std::stringstream ss;//请注意,ss是正确构建的(可以通过将ss.str()保存到文件中来显示)。
    
    ss我通过实验详细阐述了一个相当简单的解决方法,我很惊讶没有人知道(我在网上没有发现类似的方法)

    N.m.的尝试性回答给出了一个很好的提示,提到了特定于平台的功能
    \u setmode
    。它“按设计”做了什么(根据和)设置文件转换模式,这是根据流程处理输入和输出流的方式。但同时,它使使用
    std::ostream
    /
    std::istream
    无效,但规定对格式正确的输入和输出流使用
    std::wostream
    /
    std::wistream

    例如,使用setmode(\u fileno(stdout),\u O\u文本)
    导致了
    std::wcout
    现在可以很好地将
    std::wstring
    输出为UTF-8,但是
    std::cout
    打印出垃圾字符,即使是在ASCII参数上。但是我希望能够主要使用
    std::string
    ,尤其是
    std::cout
    进行输出。正如我所提到的,这是一种罕见的情况
    std::cout
    的格式化失败,因此只有在打印出可能导致此问题的字符串(索引至少为1024的潜在多字符编码字符)的情况下,我才希望使用特殊的输出函数,例如
    couttf8string(string s)

    默认值(未翻译)
    \u setmode
    的模式是
    \u O_BINARY
    。我们可以临时切换模式。那么为什么不切换到
    \u O_u8; text
    ,将UTF-8编码的
    std::string
    对象转换为
    std::wstring
    ,在其上使用
    std::wcout
    ,然后切换回
    \u O_BINARY
    ?为了保持平台独立,可以不在Windows上时,只需定义通常的
    std::cout
    调用。以下是代码:

    #if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__)
    #include <fcntl.h> // Also includes the non-standard file <io.h>
                       // (POSIX compatibility layer) to use _setmode on Windows NT.
    #ifndef _O_U8TEXT // Some GCC distributions such as TDM-GCC 9.2.0 require this explicit
                      // definition since, depending on __MSVCRT_VERSION__, they might
                      // not define it.
    #define _O_U8TEXT 0x40000
    #endif
    #endif
    
    void coutUtf8String(string s) {
    #if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__)
        if (s.length() > 1024) {
            // Set translation mode of wcout to UTF-8, renders cout unusable "by design"
            // (see https://developercommunity.visualstudio.com/solutions/411680/view.html).
            if (_setmode(STDOUT_FILENO, _O_U8TEXT) != -1) {
                wcout << utf8toWide(s) << flush; // We must flush before resetting the mode.
                 // Set translation mode of wcout to untranslated, renders cout usable again.
                _setmode(STDOUT_FILENO, _O_BINARY);
            } else
                // Let's use wcout anyway. Since no sink (such as Eclipse's console
                // window) is attached when _setmode fails, and such sinks seem to be
                // the cause for wcout to fail in default mode. The UI console view
                // is filled properly like this, regardless of translation modes.
                wcout << utf8toWide(s) << flush;
        } else
            cout << s << flush;
    #else
        cout << s << flush;
    #endif
    }
    
    wstring utf8toWide(const char* in) {
        wstring out;
        if (in == nullptr)
            return out;
        uint32_t codepoint = 0;
        while (*in != 0) {
            unsigned char ch = static_cast<unsigned char>(*in);
            if (ch <= 0x7f)
                codepoint = ch;
            else if (ch <= 0xbf)
                codepoint = (codepoint << 6) | (ch & 0x3f);
            else if (ch <= 0xdf)
                codepoint = ch & 0x1f;
            else if (ch <= 0xef)
                codepoint = ch & 0x0f;
            else
                codepoint = ch & 0x07;
            ++in;
            if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff)) {
                if (codepoint > 0xffff) {
                    out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
                    out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
                } else if (codepoint < 0xd800 || codepoint >= 0xe000)
                    out.append(1, static_cast<wchar_t>(codepoint));
            }
        }
        return out;
    }
    
    #如果已定义(_WIN32)|已定义(WIN32)|已定义(uu CYGWIN_uu)
    #include//还包括非标准文件
    //(POSIX兼容层)在Windows NT上使用_setmode。
    #ifndef_O_U8TEXT//一些GCC发行版,如TDM-GCC 9.2.0,需要明确的
    //d