我读到和听说 C + + 11支持 Unicode,关于这个有几个问题:
std::string
C + + 11有一对用于 Unicode 的 新的字符串类型。
不幸的是,标准库中对非统一编码(如 UTF-8)的支持仍然很差。例如,没有很好的方法来获得 UTF-8字符串的长度(以代码点为单位)。
由于 Unicode NUL (U + 0000)在 UTF-8中是空字节,而且这是在 UTF-8中出现空字节的唯一方式,因此可以安全地将 UTF-8存储在 std::string中(或者在 char[]或 char*中)。因此,您的 UTF-8字符串将根据所有 C 和 C + + 字符串函数正确地终止,并且您可以使用 C + + iostream (包括 std::cout和 std::cerr,只要您的区域设置是 UTF-8)将它们吊起来。
char[]
char*
std::cout
std::cerr
对于 UTF-8,使用 std::string不能做的是以代码点的形式获取长度。std::string::size()将告诉您 字节中的字符串长度,它只等于当您位于 UTF-8的 ASCII 子集中时的代码点数。
std::string::size()
如果您需要在 代码点级别操作 UTF-8字符串(即不仅仅是存储和打印它们) ,或者如果您正在处理 UTF-16,它可能有许多内部空字节,那么您需要查看宽字符串类型。
标准图书馆不支持 Unicode (对于支持的任何合理含义)。
std::string并不比 std::vector<char>好: 它完全不受 Unicode (或任何其他表示/编码)的影响,只是把它的内容当作字节的 一团。
std::vector<char>
如果你只需要存储和连接 一团团,它的工作相当不错,但一旦你希望 Unicode 的功能(数字 代码点,数字 图解等) ,你是运气不佳。
据我所知,这方面唯一的综合库是 重症监护室。不过,C + + 接口是从 Java 接口派生而来的,因此它远非惯用语。
C++标准程式库对 unicode 的支持程度如何?
糟透了。
快速浏览一下可能提供 Unicode 支持的库设施,我得到了以下列表:
我认为除了第一个以外,所有人都提供了可怕的支持。我稍微绕过你的其他问题,再回到更多细节上来。
std::string是否做了它应该做的?
是的。根据 C + + 标准,这是 std::string及其兄弟应该做的:
类模板 basic_string描述的对象可以存储一个序列,该序列由不同数量的任意类似字符的对象组成,序列的第一个元素位于零。
basic_string
那么,std::string就可以做到这一点。它提供任何 Unicode 特有的功能吗? 没有。
应该吗?也许不会。std::string可以作为 char对象的序列。这很有用; 唯一的烦恼是它是一个非常低级的文本视图,而标准 C + + 并不提供更高级的视图。
char
怎么用?
将它作为 char对象的序列使用; 假装它是其他什么东西注定会以痛苦结束。
潜在的问题在哪里?
到处都是? 让我看看..。
字符串库
字符串库为我们提供了 basic_string,它仅仅是标准称为“类 char 对象”的序列。我叫他们代号单位。如果您想要一个文本的高级视图,这不是您要寻找的。这是一个适合序列化/反序列化/存储的文本视图。
它还提供了一些来自 C 库的工具,可以用来弥补狭义世界和 Unicode 世界之间的差距: c16rtomb/mbrtoc16和 c32rtomb/mbrtoc32。
c16rtomb
mbrtoc16
c32rtomb
mbrtoc32
本地化库
本地化库仍然认为其中一个“类字符对象”等于一个“字符”。这当然是愚蠢的,并且使得除了像 ASCII 这样的 Unicode 小子集之外,很多事情都不可能正常工作。
例如,考虑一下标准在 <locale>报头中所称的“方便接口”:
<locale>
template <class charT> bool isspace (charT c, const locale& loc); template <class charT> bool isprint (charT c, const locale& loc); template <class charT> bool iscntrl (charT c, const locale& loc); // ... template <class charT> charT toupper(charT c, const locale& loc); template <class charT> charT tolower(charT c, const locale& loc); // ...
如何期望这些函数中的任何一个正确地分类,例如,U + 1 F34Csomething something something,就像在 u8"🍌"或 u8"\U0001F34C"中那样?它不可能工作,因为这些函数只需要一个代码单元作为输入。
u8"🍌"
u8"\U0001F34C"
如果只使用 char32_t,这可以在适当的语言环境中工作: U'\U0001F34C'是 UTF-32中的单个代码单元。
char32_t
U'\U0001F34C'
但是,这仍然意味着您只能获得 toupper和 tolower的简单大小写转换,例如,对于某些德语语言环境来说,这还不够好: “ ß”大写到“ SS”,但是 toupper只能返回一个 性格代码单元。
toupper
tolower
接下来,wstring_convert/wbuffer_convert和标准代码转换方面。
wstring_convert
wbuffer_convert
wstring_convert用于在一个给定编码的字符串之间转换为另一个给定编码的字符串。此转换涉及两种字符串类型,标准称为字节字符串和宽字符串。由于这些术语确实具有误导性,我更喜欢分别使用“序列化”和“反序列化”,而不是“短剑”。
要在两者之间转换的编码由作为模板类型参数传递给 wstring_convert的 codecvt (代码转换 facet)决定。
wbuffer_convert执行类似的功能,但作为包装 字节序列化流缓冲区的 很宽反序列化流缓冲区。任何 I/O 都是通过底层 字节序列化流缓冲区执行的,其中包含与 codecvt 参数给出的编码之间的转换。将序列化写入该缓冲区,然后从该缓冲区写入,并将读入读入缓冲区,然后从该缓冲区反序列化。
该标准提供了一些 codecvt 类模板,以便与这些工具一起使用: codecvt_utf8、 codecvt_utf16、 codecvt_utf8_utf16和一些 codecvt专门化。这些标准方面一起提供了以下所有转换。(注意: 在下面的列表中,左边的编码总是序列化的字符串/stream buf,右边的编码总是反序列化的字符串/stream buf; 标准允许双向转换)。
codecvt_utf8
codecvt_utf16
codecvt_utf8_utf16
codecvt
codecvt_utf8<char16_t>
codecvt_utf8<wchar_t>
sizeof(wchar_t) == 2
codecvt_utf8<char32_t>
codecvt<char32_t, char, mbstate_t>
sizeof(wchar_t) == 4
codecvt_utf16<char16_t>
codecvt_utf16<wchar_t>
codecvt_utf16<char32_t>
codecvt_utf8_utf16<char16_t>
codecvt<char16_t, char, mbstate_t>
codecvt_utf8_utf16<wchar_t>
codecvt<wchar_t, char_t, mbstate_t>
codecvt<char, char, mbstate_t>
其中有几个是有用的,但这里有很多令人尴尬的东西。
首先,我的天啊,那个命名方案真是一团糟。
然后,有很多 UCS-2的支持。UCS-2是来自 Unicode 1.0的编码,1996年被取代,因为它只支持基本的多语言平面。为什么委员会认为应该关注一种20年前就被取代的编码方式,我不知道。支持更多的编码并不是坏事,但 UCS-2在这里出现得太频繁了。
我认为 char16_t显然是用来存储 UTF-16代码单元的。然而,这只是标准的一部分,并不这么认为。codecvt_utf8<char16_t>与 UTF-16无关。例如,wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")可以很好地编译,但是无条件地失败: 输入将被视为 UCS-2字符串 u"\xD83C\xDF4C",它不能被转换为 UTF-8,因为 UTF-8不能编码范围为0xD800-0xDFFF 的任何值。
char16_t
wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")
u"\xD83C\xDF4C"
在 UCS-2方面,仍然没有办法将 UTF-16字节流读入带有这些方面的 UTF-16字符串。如果有一个 UTF-16字节的序列,则不能将其反序列化为 char16_t字符串。这令人惊讶,因为它或多或少是一种身份转换。但是,更令人惊讶的是,它支持将 UTF-16流反序列化为带有 codecvt_utf16<char16_t>的 UCS-2字符串,这实际上是一种有损转换。
UTF-16-as-byte 支持相当不错: 它支持从 BOM 检测 endianness,或者在代码中显式选择它。它还支持使用和不使用 BOM 生产产出。
还有一些更有趣的转换可能性不存在。无法将 UTF-16字节流或字符串反序列化为 UTF-8字符串,因为反序列化形式从来不支持 UTF-8。
在这里,窄/宽的世界与 UTF/UCS 世界是完全分离的。旧式窄/宽编码和任何 Unicode 编码之间没有转换。
输入/输出库
使用上述 wstring_convert和 wbuffer_convert工具,可以使用 I/O 库读写 Unicode 编码中的文本。我认为标准库的这一部分不需要支持太多其他内容。
正则表达式库
我以前已经阐述过关于堆栈溢出的 正则表达式和 Unicode问题。我不会在这里重复所有这些观点,只是说明 C + + 正则表达式不支持一级 Unicode,这是使它们可用的最低限度,而不必到处使用 UTF-32。
就这样?
是的,就是这样。这就是现有的功能。有许多 Unicode 的功能,这是无处可见的正常化或文本分割算法。
U + 1 F4A9 有没有什么方法可以在 C + + 中获得更好的 Unicode 支持?
通常的怀疑对象: 重症监护室和 推进,现场。
毫无疑问,字节串是一个字节串,也就是 char对象。但是,与 宽字符串字面值不同(宽字符串字面值总是 wchar_t对象的数组) ,在此上下文中的“宽字符串”不一定是 wchar_t对象的字符串。事实上,标准从未明确定义“宽字符串”的含义,因此我们只能从用法中猜测其含义。由于标准术语是草率和混乱的,我使用自己的,在清晰的名义。
wchar_t
像 UTF-16这样的编码可以存储为 char16_t序列,这些序列没有字节顺序; 或者它们可以存储为字节序列,这些字节序列有字节顺序(每个连续的字节对可以根据字节顺序表示不同的 char16_t值)。标准支持这两种形式。char16_t序列对程序中的内部操作更有用。字节序列是与外部世界交换此类字符串的方式。因此,我将使用“序列化”和“反序列化”来代替“ byte”和“ wide”。
如果你想说“但是窗户!”保持 。自 Windows2000以来的所有 Windows 版本都使用 UTF-16。
Something 是的,我知道 伟大的 Eszett(something) ,但是即使您在一夜之间将所有德语地区都改为 ß 大写,仍然有很多其他情况下会失败。试试大写 U + FB 00 something s something something something something something something something something。没有 something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something。或者 U + 01 F0 something s something something something something something something something something something something something something something something something 没有预先组合的大写字母,它只是大写字母 J 和一个组合的 caron。
但是,有一个非常有用的库,称为 Tiny-utf8,它基本上是 std::string/std::wstring的 临时代课老师。它旨在填补仍然缺失的 utf8-string 容器类的空白。
std::wstring
这可能是“处理”utf8字符串的最舒适的方法(也就是说,没有 unicode 标准化和类似的东西)。您可以轻松地操作 密码点,而字符串保持编码为运行长度编码的 char。