【问题标题】:How to print Unicode character in C++?如何在 C++ 中打印 Unicode 字符?
【发布时间】:2021-12-26 04:36:45
【问题描述】:

我正在尝试打印俄语“ф”(U+0444西里尔小写字母 EF)字符,该字符被赋予十进制代码1092。使用 C++,我怎样才能打印出这个字符?我原以为以下内容会起作用,但是...

int main (){
   wchar_t f = '1060';
   cout << f << endl;
}

【问题讨论】:

  • 请注意,问题有两个方面(至少对于有效的 C++ 程序而言):在代码中表达字符,并将其正确传递给 std::cout。 (即使正确完成了这两个步骤,在 std::cout 连接的任何内容中正确显示字符也是完全不同的。)
  • 这能回答你的问题吗? Unicode encoding for string literals in C++11

标签: c++ unicode iostream cout wchar-t


【解决方案1】:

要表示字符,您可以使用通用字符名称 (UCN)。字符 'ф' 的 Unicode 值是 U+0444,所以在 C++ 中你可以写成 '\u0444' 或 '\U00000444'。此外,如果源代码编码支持此字符,那么您可以直接在源代码中编写它。

// both of these assume that the character can be represented with
// a single char in the execution encoding
char b = '\u0444';
char a = 'ф'; // this line additionally assumes that the source character encoding supports this character

打印这些字符取决于您要打印的内容。如果要打印到 Unix 终端仿真器,终端仿真器正在使用支持该字符的编码,并且该编码与编译器的执行编码相匹配,那么您可以执行以下操作:

#include <iostream>

int main() {
    std::cout << "Hello, ф or \u0444!\n";
}

这个程序要求'ф'可以用单个字符表示。在 OS X 和大多数现代 Linux 安装上,这都可以正常工作,因为源、执行和控制台编码都将是 UTF-8(支持所有 Unicode 字符)。

对于 Windows,事情变得更加困难,并且通过不同的权衡有不同的可能性。

如果您不需要可移植代码(您将使用 wchar_t,在所有其他平台上确实应该避免使用),最好的方法可能是将输出文件句柄的模式设置为仅采用 UTF-16数据。

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

int main() {
    _setmode(_fileno(stdout), _O_U16TEXT);
    std::wcout << L"Hello, \u0444!\n";
}

可移植代码更难。

【讨论】:

  • ?我很确定 '\u0444' 不适合 char 除非编译器已将 char 提升为 int,但如果你想要这种行为,你应该使用 int。
  • 如果执行字符集是 ISO-8859-5,@EdwardFalk \u0444 将适合 8 位 char。具体来说,它将是字节 0xE4。请注意,我并不是说使用这样的执行字符集是一种好习惯,我只是在描述 C++ 的工作原理。
  • 啊,你是说编译器会将\u0444识别为unicode字符,并将其转换为流行字符集,结果将适合一个字节?我不知道它会那样做。
  • 是的。这就是使用\u 与使用\x 不同的原因。
  • 在我的带有终结器终端和 g++ 5.4.0 的 lubuntu 16 笔记本电脑上不起作用,但使用 std::string 可以工作
【解决方案2】:

使用-std=c++11编译时,可以很简单

  const char *s  = u8"\u0444";
  cout << s << endl;

【讨论】:

  • 让我推荐Boost.Nowide以便携的方式将UTF-8字符串打印到终端,所以上面的代码几乎不会改变。
  • @ybungalobill,您的评论值得单独回答。你介意创建一个吗?
  • 仅供参考:\uXXXX\UXXXXXXXX 被称为 universal-character-nameu8"..." 形式的字符串文字是 UTF-8 字符串文字。两者都在标准中指定。
【解决方案3】:

最终,这完全取决于平台。不幸的是,标准 C++ 对 Unicode 的支持很差。对于 GCC,您必须将其设为窄字符串,因为它们使用 UTF-8,而 Windows 需要宽字符串,您必须输出到 wcout

// GCC
std::cout << "ф";
// Windoze
wcout << L"ф";

【讨论】:

  • IIRC,Unicode 转义是\uXXXX,其中XXXX 代表hex 位。不幸的是,这会将 U+FFFF 之后的所有字符都排除在外。
  • @Mike:如果你想通过 FFFF,你可以通过使用 \u 的两个实例自己生成一个 UTF-16 代理对来做到这一点,至少在 Windows 上是这样。
  • @BillyONeal 您不在 C++ 中使用代理代码点(实际上完全禁止代理代码点)。您使用格式\UXXXXXXXX
  • GCC 不一定要使用 UTF-8,并且适用于 Windows。 std::wcout 也是 Windows 之外的一个选项。
  • @Jam '\u0400' 是一个窄字符文字。您似乎假设 \u0400 存在于执行字符集中。根据 N3242 [lex.ccon]/5:“通用字符名被转换为命名字符在适当执行字符集中的编码。如果没有这样的编码,通用字符名是翻译成实现定义的编码。”
【解决方案4】:

如果您使用 Windows(注意,我们使用的是 printf(),而不是 cout):

//Save As UTF8 without signature
#include <stdio.h>
#include<windows.h>
int main (){
    SetConsoleOutputCP(65001); 
    printf("ф\n");
}

不是 Unicode,但可以使用 - 1251 而不是 UTF8:

//Save As Windows 1251
#include <iostream>
#include<windows.h>
using namespace std;
int main (){
    SetConsoleOutputCP(1251); 
    cout << "ф" << endl;
}

【讨论】:

  • SetConsoleOutputCP() 在这种情况下有一个更好的名字。
  • 仅供参考:Windows 中默认的西里尔控制台编码是 OEM 866。
【解决方案5】:

此代码适用于 Linux(C++11、geany、g++ 7.4.0):

#include <iostream>

using namespace std;


int utf8_to_unicode(string utf8_code);
string unicode_to_utf8(int unicode);


int main()
{
    cout << unicode_to_utf8(36) << '\t';
    cout << unicode_to_utf8(162) << '\t';
    cout << unicode_to_utf8(8364) << '\t';
    cout << unicode_to_utf8(128578) << endl;

    cout << unicode_to_utf8(0x24) << '\t';
    cout << unicode_to_utf8(0xa2) << '\t';
    cout << unicode_to_utf8(0x20ac) << '\t';
    cout << unicode_to_utf8(0x1f642) << endl;

    cout << utf8_to_unicode("$") << '\t';
    cout << utf8_to_unicode("¢") << '\t';
    cout << utf8_to_unicode("€") << '\t';
    cout << utf8_to_unicode("?") << endl;

    cout << utf8_to_unicode("\x24") << '\t';
    cout << utf8_to_unicode("\xc2\xa2") << '\t';
    cout << utf8_to_unicode("\xe2\x82\xac") << '\t';
    cout << utf8_to_unicode("\xf0\x9f\x99\x82") << endl;

    return 0;
}


int utf8_to_unicode(string utf8_code)
{
    unsigned utf8_size = utf8_code.length();
    int unicode = 0;

    for (unsigned p=0; p<utf8_size; ++p)
    {
        int bit_count = (p? 6: 8 - utf8_size - (utf8_size == 1? 0: 1)),
            shift = (p < utf8_size - 1? (6*(utf8_size - p - 1)): 0);

        for (int k=0; k<bit_count; ++k)
            unicode += ((utf8_code[p] & (1 << k)) << shift);
    }

    return unicode;
}


string unicode_to_utf8(int unicode)
{
    string s;

    if (unicode>=0 and unicode <= 0x7f)  // 7F(16) = 127(10)
    {
        s = static_cast<char>(unicode);

        return s;
    }
    else if (unicode <= 0x7ff)  // 7FF(16) = 2047(10)
    {
        unsigned char c1 = 192, c2 = 128;

        for (int k=0; k<11; ++k)
        {
            if (k < 6)  c2 |= (unicode % 64) & (1 << k);
            else c1 |= (unicode >> 6) & (1 << (k - 6));
        }

        s = c1;    s += c2;

        return s;
    }
    else if (unicode <= 0xffff)  // FFFF(16) = 65535(10)
    {
        unsigned char c1 = 224, c2 = 128, c3 = 128;

        for (int k=0; k<16; ++k)
        {
            if (k < 6)  c3 |= (unicode % 64) & (1 << k);
            else if (k < 12) c2 |= (unicode >> 6) & (1 << (k - 6));
            else c1 |= (unicode >> 12) & (1 << (k - 12));
        }

        s = c1;    s += c2;    s += c3;

        return s;
    }
    else if (unicode <= 0x1fffff)  // 1FFFFF(16) = 2097151(10)
    {
        unsigned char c1 = 240, c2 = 128, c3 = 128, c4 = 128;

        for (int k=0; k<21; ++k)
        {
            if (k < 6)  c4 |= (unicode % 64) & (1 << k);
            else if (k < 12) c3 |= (unicode >> 6) & (1 << (k - 6));
            else if (k < 18) c2 |= (unicode >> 12) & (1 << (k - 12));
            else c1 |= (unicode >> 18) & (1 << (k - 18));
        }

        s = c1;    s += c2;    s += c3;    s += c4;

        return s;
    }
    else if (unicode <= 0x3ffffff)  // 3FFFFFF(16) = 67108863(10)
    {
        ;  // actually, there are no 5-bytes unicodes
    }
    else if (unicode <= 0x7fffffff)  // 7FFFFFFF(16) = 2147483647(10)
    {
        ;  // actually, there are no 6-bytes unicodes
    }
    else  ;  // incorrect unicode (< 0 or > 2147483647)

    return "";
}

更多:

【讨论】:

    【解决方案6】:

    '1060' 是四个字符,在标准下不会编译。如果您的宽字符与 Unicode 1:1 匹配(检查您的语言环境设置),您应该将字符视为数字。

    int main (){
        wchar_t f = 1060;
        wcout << f << endl;
    }
    

    【讨论】:

    • 我认为这是 iostreams 的要点之一:它会通过重载的operator &lt;&lt; 检测类型并做正确的事。没那么多,我猜?
    • @Jam 这在很大程度上取决于系统。您使用的是什么操作系统?
    • '1060'int 类型的多字符字符文字,在标准 C++ 下是完全合法的。它的价值是实现定义的。大多数实现将采用字符的值并将它们连接起来以产生单个整数值。这些有时用于所谓的“FourCC”。
    • 也许你会惊讶于完全合法的代码有多少警告。 C++ 标准说“包含多个 c-char 的普通字符文字是多字符文字。多字符文字具有 int 类型和实现定义的值。” [lex.ccon] 2.14.3/1
    • @MikeDeSimone "我用过的每个非 Mac 编译器都至少发出一个警告" 因为它是 1) 几乎从未在非 Mac 系统上故意使用 2)不是便携式构造
    【解决方案7】:

    我需要在 UI 中显示字符串并将其保存到 xml 配置文件中。上面指定的格式适用于 c++ 中的字符串,我想补充一下,我们可以通过将“\u”替换为“”并添加一个“;”来获得特殊字符的 xml 兼容字符串最后。

    例如: C++ : "\u0444" --> XML : "&amp;#x0444;"

    【讨论】:

      【解决方案8】:

      在 Linux 中,我可以这样做:

      std::cout << "ф";
      

      我只是从here 复制粘贴字符,至少我尝试的随机样本没有失败。

      【讨论】:

        【解决方案9】:

        Linux 中的另一种解决方案:

        string a = "Ф";
        cout << "Ф = \xd0\xa4 = " << hex
             << int(static_cast<unsigned char>(a[0]))
             << int(static_cast<unsigned char>(a[1])) << " (" << a.length() << "B)" << endl;
        
        string b = "√";
        cout << "√ = \xe2\x88\x9a = " << hex
             << int(static_cast<unsigned char>(b[0]))
             << int(static_cast<unsigned char>(b[1]))
             << int(static_cast<unsigned char>(b[2])) << " (" << b.length() << "B)" << endl;
        

        【讨论】:

          【解决方案10】:

          特别感谢回答here 或多或少相同的问题。

          对我来说,我只需要 setlocale(LC_ALL, "en_US.UTF-8");

          然后,我什至可以使用原始的wchar_t 字符。

          【讨论】:

            【解决方案11】:

            在 Linux 上,Unicode 字符(UTF-16 / UTF-32)可以转换为 UTF-8 并打印到 std::cout。我用了这些functions

            【讨论】:

              猜你喜欢
              • 2013-02-03
              • 1970-01-01
              • 2013-06-01
              • 1970-01-01
              • 2013-11-23
              相关资源
              最近更新 更多