将char32_t打印到控制台

3

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

下面的代码打印十六进制值:

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

我想要一个不受操作系统限制的解决方案(如果可能的话)。我使用的是Linux x86_64操作系统。 - Wojciech Danilo
不可能。在Windows上需要进行重大修改才能运行。此外,我认为wcout不应该打印除charwchar_t以外的任何字符。char32_t也不是其中之一。 - Alexey Frunze
哦:( 但是在所有平台上都可以编写u32编码文件吗?因此,如果我想要使用*cout打印任何“可读”的内容,我是否需要将这些字符转换为utf8(如果可能的话)? - Wojciech Danilo
编码和转换并不是问题,你总是可以做到。获取可读文本的问题在于拥有并能够选择适当的Unicode字体。Windows控制台在这方面相当有缺陷。请参见我的这个答案,并跟随其中的链接。至于你的例子,请看这个 - Alexey Frunze
2个回答

4

首先,我认为wcout不应该打印除charwchar_t之外的任何字符。char32_t也不是。

下面是一个打印单个wchar_t的示例程序:

#include <iostream>

using namespace std;

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

输出 (ideone):

A

目前,即使在主要的操作系统中,控制台也无法提供一致的Unicode输出。通过coutwcoutprintf()wprintf()等简单的Unicode文本输出,在Windows上不会起作用,除非进行重大改进。在Windows控制台中获取可读的Unicode文本的问题在于拥有并能够选择适当的Unicode字体。在这方面,Windows控制台相当失灵。请参见我的这个答案并跟随其中的链接。


3

我知道这已经很老了,但我不得不自己解决它,然后你就可以开始了。 思路是在UTF-8和UTF-32编码之间切换Unicode:您可以cout 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;
}

如果您愿意,可以在类中、构造函数中、模板中或任何您喜欢的方式中实现此函数。

使用字符数组重载运算符。

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字符进行测试... 此外,这也会因字节序而异,但我相信您可以在这里找到解决方案。

根据输出环境是否支持UTF8,您可以使用u8字符串进行cout。C++标准库具有char32_t到UTF8的转换,因此您在那里是在重新发明轮子。此外,您不应尝试以这种方式重载operator<<,因为它不会被ADL找到。 - M.M
1
  1. 我正在回答一个关于Linux的问题,通常Linux环境支持UTF-8。
  2. 这个答案不应该解释codecvt或其他std设施,也不要发明新东西。只需用非标准低级代码回答一个六年未解答的问题,就像答案中所写的那样。
  3. 这个答案只是解释了数百人可能在我之前已经想过的一个想法,不涉及任何与代码管理和命名空间相关的内容。尽管如此,对于你所强调的问题还有一个简单的解决方案,我相信你很清楚。
- dteod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接