如何在 C++ 中打印 Unicode 字符

How to print Unicode character in C++

提问人:James Raitsev 提问时间:8/18/2012 最后编辑:Peter MortensenJames Raitsev 更新时间:5/16/2023 访问量:239343

问:

我正在尝试打印一个俄语“ф”(U+0444 西里尔文小写字母 EF)字符,该字符的代码为十进制 1092。使用 C++,如何打印出此字符?我本来以为以下内容会起作用,但是......

int main (){
   wchar_t f = '1060';
   cout << f << endl;
}
C++ Unicode IOSTREAM cout wchar-t

评论

2赞 Luc Danton 8/18/2012
请注意,问题有两个方面(至少当涉及到有效的 C++ 程序时):在代码中表达字符,并将其正确地传递给 。(即使这两个步骤正确完成,在所连接的任何东西中正确显示字符也完全是另一回事。std::coutstd::cout
0赞 M.J. Rayburn 6/24/2021
这回答了你的问题吗?C++ 中字符串文字的 Unicode 编码 11

答:

12赞 Puppy 8/18/2012 #1

归根结底,这完全取决于平台。不幸的是,Unicode支持在标准C++中非常差。对于 GCC,您必须将其设置为窄字符串,因为它们使用 UTF-8,而 Windows 需要宽字符串,并且您必须输出到 .wcout

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

评论

1赞 Mike DeSimone 8/18/2012
IIRC,Unicode 转义是十六进制数字的位置。不幸的是,这遗漏了 U+FFFF 之后的所有角色。\uXXXXXXXX
1赞 Billy ONeal 8/18/2012
@Mike:如果你想过去的 FFFF,你可以通过使用 的两个实例自己生成一个 UTF-16 代理项对来实现,至少在 Windows 上是这样。\u
9赞 bames53 8/18/2012
@BillyONeal 您不要在C++中使用代理代码点(实际上完全禁止代理代码点)。您使用格式 .\UXXXXXXXX
2赞 Luc Danton 8/18/2012
GCC 不绑定使用 UTF-8,并且可用于 Windows。 也是 Windows 之外的一个选项。std::wcout
2赞 curiousguy 8/18/2012
@Jam 是一个狭义字符的文字。您似乎认为它存在于执行字符集中。根据 N3242 [lex.ccon]/5:“通用字符名称被转换为所命名字符的适当执行字符集中的编码。如果没有这样的编码,则通用字符名称将转换为实现定义的编码。'\u0400'\u0400
3赞 Mike DeSimone 8/18/2012 #2

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

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

评论

0赞 Mike DeSimone 8/18/2012
我认为这是 iostreams 的要点之一:它会通过重载和做正确的事来检测类型。我猜没那么多吧?operator <<
0赞 Mark Ransom 8/18/2012
@Jam这在很大程度上取决于系统。您使用的是什么操作系统?
4赞 bames53 8/18/2012
'1060'是类型的多字符字符文本,在标准 C++ 下是完全合法的。不过,它的价值是实现定义的。大多数实现将获取字符的值并将它们连接起来以生成单个整数值。这些有时用于所谓的“FourCC”。int
3赞 bames53 8/18/2012
也许你会惊讶于有多少关于完全合法的法典的警告。C++ 标准说:“包含多个 c 字符的普通字符文本是多字符文本。多字符文本具有类型 int 和实现定义的值。[lex.ccon] 2.14.3/1
2赞 curiousguy 8/18/2012
@MikeDeSimone“我使用过的每个非 Mac 编译器都至少发出警告”,因为它是 1) 几乎从未在非 Mac 系统上故意使用过 2) 不是可移植结构
78赞 bames53 8/18/2012 #3

若要表示字符,可以使用通用字符名称 (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";
}

可移植代码更难。

评论

6赞 Edward Falk 9/5/2016
?我很确定“\u0444”不适合 char,除非编译器将 char 提升为 int,但如果您想要这种行为,您应该使用 int。
1赞 bames53 9/5/2016
@EdwardFalk \u0444 如果执行字符集为 ISO-8859-5,则适合 8 位。具体来说,它将是字节0xE4。请注意,我并不是说使用这样的执行字符集是一种好的做法,我只是在描述C++的工作原理。char
1赞 Edward Falk 9/6/2016
啊,你是说编译器会将 \u0444 识别为 unicode 字符,并将其转换为常用字符集,结果将适合一个字节?我不知道它会这样做。
1赞 bames53 3/6/2017
是的。这就是为什么 using 与 using 不同的原因。\u\x
0赞 Austin_Anderson 10/16/2017
在我的带有终结者终端和 G++ 5.4.0 的 Lubuntu 16 笔记本电脑上不起作用,但使用 std::string 有效
18赞 James Raitsev 8/19/2012 #4

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

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

评论

4赞 Yakov Galka 8/30/2012
让我推荐 Boost.Nowide 以可移植的方式将 UTF-8 字符串打印到终端,因此上面的代码几乎不会改变。
2赞 Jorge Leitao 1/6/2015
@ybungalobill,您的评论本身就应该得到答复。你介意创建一个吗?
1赞 ynn 12/27/2019
只是为了我的笔记:并被称为通用字符名称。该格式的字符串文本是 UTF-8 字符串文本。两者都在标准中指定。\uXXXX\UXXXXXXXXu8"..."
8赞 vladasimovic 9/29/2013 #5

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

// Save as UTF-8 without a signature
#include <stdio.h>
#include<windows.h>

int main (){
    SetConsoleOutputCP(65001);
    printf("ф\n");
}

它不是 Unicode,但它正在工作 - Windows-1251 而不是 UTF-8

// Save as Windows 1251
#include <iostream>
#include<windows.h>

using namespace std;

int main (){
    SetConsoleOutputCP(1251);
    cout << "ф" << endl;
}

评论

0赞 Cong Ma 4/13/2017
在这种情况下,SetConsoleOutputCP() 的名称要好得多。
0赞 Qwertiy 12/20/2020
仅供参考:Windows 中的默认西里尔语控制台编码是 OEM 866。
0赞 Eugene Kartoyev 9/24/2022
我不得不使用 - SetConsoleOutputCP(CP_UTF8);和 printf(u8“Привет мир\n”);
0赞 quanta 1/9/2017 #6

在 Linux 中,我只能做:

std::cout << "ф";

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

0赞 VoyciecH 12/6/2018 #7

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;
1赞 MGR 1/29/2019 #8

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

例如:

C++: “\u0444” → XML :"&#x0444;"

10赞 Iro 6/26/2019 #9

此代码适用于 Linux(C++11GeanyGCC 7.4 (. 2018-12-06)):g++

#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 "";
}

更多:

1赞 Andrew 9/14/2020 #10

特别感谢这里的答案或多或少是相同的问题。

对我来说,我所需要的只是 setlocale(LC_ALL, “en_US.UTF-8“);

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