C++ unicode 字符打印

Posted

技术标签:

【中文标题】C++ unicode 字符打印【英文标题】:C++ unicode characters printing 【发布时间】:2013-06-01 10:30:33 【问题描述】:

我需要在 Linux 终端上使用 iostream 打印一些 unicode 字符。奇怪的事情发生了。当我写的时候:

cout << "\u2780";

我得到:,这几乎正是我想要的。但是,如果我写:

cout << '\u2780';

我得到:14851712

问题是,我不知道在编译时要打印的确切字符。因此,我想做类似的事情:

int x;
// some calculations...
cout << (char)('\u2780' + x);

打印:。使用 wcoutwchar_t 也不起作用。如何正确打印?

根据我在 Internet 上的发现,我使用直接来自 Debian Wheezy 存储库的 g++ 4.7.2 编译器似乎很重要。

【问题讨论】:

正在使用 wchar_t 和运算符 L 吗?如果可能,请发布您的完整代码或 [sscce.org](SSCCE) 如果您不想弄乱 Unicode 编码,可以使用表格将字符串映射到 x 的可能值,而不是添加它。 How to print Unicode character in C++?的可能重复 【参考方案1】:

Unicode 字符 \u2780 超出了 char 数据类型的范围。你应该已经收到了这个编译器警告来告诉你:(至少我的 g++ 4.7.3 给出了它)

test.cpp:6:13: warning: multi-character character constant [-Wmultichar]

如果您想将 U+2780 等字符作为单个单元使用,则必须使用 Widechar 数据类型 wchar_t,或者如果您有幸能够使用 C++11,char32_tchar16_t。请注意,一个 16 位单元不足以表示所有 Unicode 字符。

如果这对您不起作用,可能是因为默认的“C”语言环境不支持非 ASCII 输出。要解决这个问题,您可以在程序开始时调用setlocale;这样您就可以输出用户语言环境支持的全部字符:(可能支持也可能不支持您使用的所有字符)

#include <clocale>
#include <iostream>

using namespace std;

int main() 
    setlocale(LC_ALL, "");
    wcout << L'\u2780';
    return 0;

【讨论】:

如果sizeof(wchar_t) &lt; 4,其他字符(SMP)当然可能有同样的问题。我建议使用char16_tchar32_t 顺便说一句。 除了编码前缀L之外,还有u8代表UTF8编码,u代表char16_tU代表char32_t setlocale 当为区域设置名称传递"" 时会设置用户的首选区域设置,这不一定是 Unicode 区域设置。 感谢@DyP,我已经添加了关于新字符数据类型的注释。 @Sventimir IIRC 他们在 C++11 中忽略了对流的 Unicode 支持;不支持带有char16_tchar32_twcout &lt;&lt; 。您必须将这些自定义转换为wchar_t 的预期编码,或者使用未格式化的输出。【参考方案2】:

当你写作时

cout << "\u2780";

编译器将 \u2780 转换为执行字符集中该字符的适当编码。那可能是 UTF-8,因此字符串最终有四个字节(三个用于字符,一个用于空终止符)。

如果您想在运行时生成字符,那么您需要某种方式在运行时执行与编译器在编译时所做的相同的到 UTF-8 的转换。


C++11 提供了一个方便的 wstring_convert 模板和 codecvt facets 可以做到这一点,但是 gcc 附带的标准库实现 libstdc++ 还没有实现它们(从 gcc 4.8 开始)。下面展示了如何使用这些功能,但您需要使用不同的标准库实现或等待 libstdc++ 实现它们。

#include <codecvt>

int main() 
  char32_t base = U'\u2780';

  std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
  std::cout << convert.to_bytes(base + 5) << '\n';

您还可以使用任何其他可用的方法来生成 UTF-8。例如,iconv、ICU 和手动使用 C++11 之前的 codecvt_byname 方面都可以工作。 (我没有展示这些示例,因为该代码比wstring_convert 允许的简单代码涉及更多。)


一种适用于少量字符的替代方法是使用文字创建一个字符串数组。

char const *special_character[] =  "\u2780", "\u2781", "\u2782",
  "\u2783", "\u2784", "\u2785", "\u2786", "\u2787", "\u2788", "\u2789" ;

std::cout << special_character[i] << '\n';

【讨论】:

【参考方案3】:

由于 C++11 §2.14.3/1,程序打印一个整数:

多字符文字或包含单个 c-char 的普通字符文字不能在执行字符集中表示,是有条件支持的,具有 int 类型,并具有实现定义的值。

执行字符集是char可以表示的,即ASCII。

你得到的是 14851712,或者是十六进制的 e29e80,它是 U+2780 的 UTF-8 表示。将 UTF-8(一种多字节编码)放入 int 是疯狂而愚蠢的,但这就是您从“有条件支持的、实现定义的”特性中得到的。

要获取 UTF-32 值,请使用 U'\u2780'。第一个U 指定char32_t 类型和UTF-32 编码(即最多31 位但没有代理对)。第二个\u 指定包含代码点的通用字符名称。要获得据称与 wcout 兼容的值,请使用 L'\u2780',但这不一定使用 Unicode 运行时值,也不一定会获得超过两个字节的存储空间。

至于可靠地操作和打印 Unicode 代码点,正如其他答案所指出的那样,C++ 标准还没有完全实现。 Joni 的回答是最好的方法,但它仍然假设编译器和用户的环境使用相同的语言环境,这通常是不正确的。

您还可以使用u8"\u2780" 在源代码中指定UTF-8 字符串,并使用std::locale::global( std::locale( "en_US.UTF-8" ) ); 之类的东西强制运行时环境为UTF-8。但这仍然有粗糙的边缘。 Joni 建议使用来自&lt;clocale&gt; 的C 接口std::setlocale 而不是来自&lt;locale&gt; 的C++ 接口std::locale::global,这是在OS X 和其他平台上的GCC 中破坏C++ 接口的一种解决方法。这些问题对平台足够敏感,以至于您的 Linux 发行版很可能已将补丁放入自己的 GCC 包中。

【讨论】:

你或我可能错过了什么,因为编译器现在敦促“U 没有在范围内声明”。 @Sventimir 显然它在 GCC 4.7.2 中不受支持,但它是 C++11 标准的一部分。只需使用L'xxx';在 Linux 中它应该做同样的事情。 使用 gcc --std=c++11 调用添加 C++11 支持也不起作用。它现在编译,但打印 char (10112) 的十进制值,而不是 char 本身。【参考方案4】:

在 Linux 中,我已经成功地以最天真的方式直接打印出任何 unicode:

std::cout << "ΐ , Α, Β, Γ, Δ, ,Θ , Λ, Ξ, ... ±, ... etc"

【讨论】:

以上是关于C++ unicode 字符打印的主要内容,如果未能解决你的问题,请参考以下文章

MessageBox 从 TCHAR 缓冲区打印额外的 unicode 字符

如何在 C++ 中将字符串从十进制代码转换为西里尔文/unicode16?

如何在 Swift 中打印 Unicode 字符

C++ 中的 Unicode 字符串索引

在 Java 中打印 Unicode 或补充字符

中文字符替换为其unicode编码值小3的字符