【问题标题】:Print char32_t to console将 char32_t 打印到控制台
【发布时间】:2013-03-29 06:33:05
【问题描述】:

如何在 C++11 中将 (cout / wcout / ...) char32_t 打印到控制台?

以下代码打印十六进制值:

u32string s2 = U"Добрый день";
for(auto x:s2){
    wcout<<(char32_t)x<<endl;
}

【问题讨论】:

  • 我想要一个独立于操作系统的解决方案(如果可能的话)。我在 Linux x86_64 上。
  • 不可能。如果没有重大黑客攻击,将无法在 Windows 上运行。另外,我不认为wcout 除了char 和wchar_t 之外不应该打印为字符。 char32_t 两者都不是。
  • oh:( 但是是否可以在所有平台上编写 u32 编码文件?所以如果我想用*cout 打印任何“可读”的东西,我必须将这些字符转换为 utf8,有可能吗?
  • 编码和转换不是问题,你总是可以做到的。获得可读文本的问题在于拥有并能够选择正确的 Unicode 字体。 Windows 的控制台在这方面非常糟糕。请参阅this answer of mine 并点击其中的链接。至于你的例子,see this.

标签: c++ unicode c++11 utf-32


【解决方案1】:

首先,我认为wcout 不应该作为字符打印,除了char 和wchar_t。 char32_t 两者都不是。

这是一个打印个人wchar_t's的示例程序:

#include <iostream>

using namespace std;

int main()
{
  wcout << (wchar_t)0x41 << endl;
  return 0;
}

输出(ideone):

A

目前,即使在主要操作系统中,也无法在控制台中获得一致的 Unicode 输出。通过cout、wcout、printf()、wprintf() 等简单的 Unicode 文本输出在没有重大黑客攻击的情况下无法在 Windows 上运行。在 Windows 控制台中获取可读的 Unicode 文本的问题在于拥有并且能够选择正确的 Unicode 字体。 Windows 的控制台在这方面非常糟糕。请参阅this answer of mine 并点击其中的链接。

【讨论】:

    【解决方案2】:

    我知道这已经很老了,但我必须自己解决它,然后就可以了。 这个想法是在 Unicode 的 UTF-8 和 UTF-32 编码之间切换:您可以计算 u8 字符串,因此只需将 UTF-32 编码的 char32_t 转换为它即可。这些是我想出的低级函数(没有现代 C++)。可能这些也可以优化:任何建议都表示赞赏。

    char* char_utf32_to_utf8(char32_t utf32, const char* buffer)
    // Encodes the UTF-32 encoded char into a UTF-8 string. 
    // Stores the result in the buffer and returns the position 
    // of the end of the buffer
    // (unchecked access, be sure to provide a buffer that is big enough)
    {
        char* end = const_cast<char*>(buffer);
        if(utf32 < 0x7F) *(end++) = static_cast<unsigned>(utf32);
        else if(utf32 < 0x7FF) {
            *(end++) = 0b1100'0000 + static_cast<unsigned>(utf32 >> 6);
            *(end++) = 0b1000'0000 + static_cast<unsigned>(utf32 & 0b0011'1111);
        }
        else if(utf32 < 0x10000){
            *(end++) = 0b1110'0000 + static_cast<unsigned>(utf32 >> 12);
            *(end++) = 0b1000'0000 + static_cast<unsigned>((utf32 >> 6) & 0b0011'1111);
            *(end++) = 0b1000'0000 + static_cast<unsigned>(utf32 & 0b0011'1111);
        } else if(utf32 < 0x110000) {
            *(end++) = 0b1111'0000 + static_cast<unsigned>(utf32 >> 18);
            *(end++) = 0b1000'0000 + static_cast<unsigned>((utf32 >> 12) & 0b0011'1111);
            *(end++) = 0b1000'0000 + static_cast<unsigned>((utf32 >> 6) & 0b0011'1111);
            *(end++) = 0b1000'0000 + static_cast<unsigned>(utf32 & 0b0011'1111);
        }
        else throw encoding_error(end);
        *end = '\0';
        return end;
    }
    

    如果你愿意,你可以在类中、在构造函数中、在模板中或任何你喜欢的地方实现这个函数。

    使用 char 数组跟随重载运算符

    std::ostream& operator<<(std::ostream& os, const char32_t* s)
    {
        const char buffer[5] {0}; // That's the famous "big-enough buffer"
        while(s && *s)
        {
            char_utf32_to_utf8(*(s++), buffer);
            os << buffer;
        }
        return os;
    }
    

    并使用 u32string

    std::ostream& operator<<(std::ostream& os, const std::u32string& s)
    {
        return (os << s.c_str());
    }
    

    使用维基百科上的 Unicode 字符运行最简单最愚蠢的测试

    int main()
    {
        std::cout << std::u32string(U"\x10437\x20AC") << std::endl;
    }
    

    导致?€ 打印在 (Linux) 控制台上。不过,这应该使用不同的 Unicode 字符进行测试...

    这也因字节顺序而异,但我相信您可以通过 this 找到解决方案。

    【讨论】:

    • "you can cout u8 strings" 取决于显示输出的环境是否支持 UTF8。 C++ 标准库有 char32_t 到 UTF8 的转换,所以你在那里重新发明轮子。此外,您不应尝试以这种方式重载 operator&lt;&lt;,因为 ADL 不会找到它
    • 1.我正在回答一个特定于 Linux 的问题,通常 Linux 环境支持 UTF-8 2。答案不应该解释 codecvt 或其他标准设施,也不应该发明新东西。只用答案中写的非标准低级代码回答一个 6 年内未回答的问题 3. 答案只是解释了数百人可能已经在我面前提出的一个想法,不涉及任何与代码管理和命名空间。不过,对于您强调的​​问题,有一个简单的解决方案,我很确定您很清楚
    猜你喜欢
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多