C++ 中的十进制到 Unicode Char

2024-01-01

输出时如何将十进制数(例如 225)转换为其对应的 Unicode 字符?我可以将 ASCII 字符从十进制转换为如下字符:

int a = 97;
char b = a;
cout << b << endl;

它输出字母“a”,但当我使用数字 225 或任何非 ascii 字符时,它只输出一个问号。


首先,将写入标准输出的字节字符串转换为可见字符的不是 C++ 程序;而是 C++ 程序。它是您的终端(或者,现在更常见的是您的终端模拟器)。不幸的是,无法询问终端如何对字符进行编码,因此需要将其配置到您的环境中;通常,这是通过设置适当的locale环境变量。

像大多数与终端有关的事情一样,如果没有使用多年的遗留软件和硬件(其中大多数最初设计时没有太多考虑细节),区域设置配置系统可能会以非常不同的方式完成。例如带重音的字母、音节表或表意文字。这就是生活。

Unicode 非常酷,但面对书写系统的计算机表示的特定历史,它也必须被部署,这意味着面对软件工程中各种坚定但完全矛盾的观点,要做出很多妥协dicho sea de paso 社区,在这个社区中,头撞比妥协更为常见。事实上 Unicode 最终或多或少变得the标准证明了其坚实的技术基础以及其推动者和设计者(尤其是马克·戴维斯)的毅力和政治技巧,尽管事实上它基本上花了二十多年才达到这一点,但我还是这么说。

这段协商和妥协历史的一个方面是,将 Unicode 字符串编码为位的方法不止一种。至少有三种方法,其中两种根据字节顺序有两个不同的版本;此外,每种编码系统都有其专门的粉丝(因此也有其教条主义的批评者)。特别是,Windows 很早就决定采用主要为 16 位的编码 UTF-16,而大多数 UNIX(类似)系统则使用可变长度的 8 到 32 位编码 UTF-8。 (从技术上讲,UTF-16 也是一种 16 位或 32 位编码,但这超出了本文的范围。)

在 Unicode 出现之前,每个国家/地区/语言都使用自己独特的 8 位编码(或者至少是那些语言使用少于 194 个字符的字母表书写的国家/地区)。因此,将编码配置为本地表示的一般配置的一部分是有意义的,例如月份名称、货币符号以及将数字的整数部分与其小数部分分隔开的字符。既然 Unicode 已经广泛(但还远未达到普遍)融合,那么语言环境包含 Unicode 编码的特定风格似乎很奇怪,因为所有风格都可以表示相同的 Unicode 字符串,并且编码更普遍地特定于特定的特定风格。正在使用的软件比民族特质。但这就是为什么在我的 Ubuntu 机器上,环境变量LANG被设定为es_ES.UTF-8不仅仅是es_ES. (Or es_PE,应该如此,只是我一直遇到该语言环境的小问题。)如果您使用的是 Linux 系统,您可能会发现类似的东西。

理论上,这意味着我的终端模拟器(konsole,碰巧,但有多种)期望看到 UTF-8 序列。而且,确实,konsole足够聪明来检查区域设置并设置其默认编码以匹配,但我可以随意更改编码(或区域设置),并且可能会导致混乱。

因此,我们假设您的区域设置和终端使用的编码实际上是同步的,它们应该位于配置良好的工作站上,然后返回到 C++ 程序。现在,C++ 程序需要弄清楚它应该使用哪种编码,然后从它使用的任何内部表示转换为外部编码。

幸运的是,C++ 标准库should如果您通过以下方式配合,请正确处理:

  1. 告诉标准库use配置的区域设置,而不是默认的区域设置C(即仅使用英语中的非重音字符)区域设置;和

  2. 使用基于字符串和iostreamswchar_t(或其他一些宽字符格式)。

如果你这样做,理论上你不需要知道什么wchar_t对您的标准库意味着什么,也不对特定位模式对您的终端模拟器意味着什么。那么让我们尝试一下:

#include <iostream>
#include <locale>

int main(int argc, char** argv) {
  // std::locale()   is the "global" locale
  // std::locale("") is the locale configured through the locale system
  // At startup, the global locale is set to std::locale("C"), so we need
  // to change that if we want locale-aware functions to use the configured
  // locale.
  // This sets the global" locale to the default locale. 
  std::locale::global(std::locale(""));

  // The various standard io streams were initialized before main started,
  // so they are all configured with the default global locale, std::locale("C").
  // If we want them to behave in a locale-aware manner, including using the
  // hopefully correct encoding for output, we need to "imbue" each iostream
  // with the default locale.
  // We don't have to do all of these in this simple example,
  // but it's probably a good idea.
  std::cin.imbue(std::locale());
  std::cout.imbue(std::locale());
  std::cerr.imbue(std::locale());
  std::wcin.imbue(std::locale());
  std::wcout.imbue(std::locale());
  std::wcerr.imbue(std::locale());

  // You can't write a wchar_t to cout, because cout only accepts char. wcout, on the
  // other hand, accepts both wchar_t and char; it will "widen" char. So it's
  // convenient to use wcout:
  std::wcout << "a acute: " << wchar_t(225) << std::endl;
  std::wcout << "pi:      " << wchar_t(960) << std::endl;
  return 0;
}

这适用于我的系统。 YMMV。祝你好运。


小旁注:我遇到过很多这样认为的人wcout自动写入“宽字符”,因此使用它会产生 UTF-16 或 UTF-32 或其他内容。事实并非如此。它产生完全相同的编码cout。区别不在于它输出什么,而在于它接受什么作为输入。事实上,它与cout因为它们都连接到同一个操作系统流,该流只能有一种编码(一次)。

您可能会问为什么需要有两个不同的iostreams。为什么不能cout刚刚接受wchar_t and std::wstring价值观?我实际上对此没有答案,但我怀疑这是不为不需要的功能付费的哲学的一部分。或类似的东西。如果你弄清楚了,请告诉我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中的十进制到 Unicode Char 的相关文章

随机推荐