Unicode在c++ 11中的支持程度
How well is Unicode supported in C++11?
我听说c++ 11支持Unicode。有几个问题:
- c++标准库如何支持Unicode?
std::string
做它应该做的吗?- 如何使用?
- 潜在的问题在哪里?
c++标准库对unicode的支持程度如何?
非常。快速浏览一下可能提供Unicode支持的库功能,我得到了这个列表:
<- 字符串库/gh><
- 本地化库/gh><
- 输入/输出库/gh>
- 正则表达式库
我认为除了第一个之外,其他的都提供了糟糕的支持。在快速浏览完你的其他问题后,我将会更详细地讨论它。
std::string
做它应该做的吗?
是的。根据c++标准,这是std::string
和它的兄弟应该做的:
类模板
basic_string
描述了可以存储由任意数量的类似char的对象组成的序列,序列的第一个元素位于位置0。
好吧,std::string
做得很好。它是否提供了任何特定于unicode的功能?没有。
应该吗?可能不会。std::string
可以作为char
对象的序列。这是有用的;唯一的烦恼是它是一个非常低级的文本视图,而标准c++没有提供一个高级视图。
如何使用?
使用它作为char
对象的序列;假装是别的东西注定会以痛苦告终。
潜在的问题在哪里?
到处都是?让我们看看…
<字符串库/em>
string标准库提供了basic_string
,它仅仅是标准所称的"类字符对象"的一个序列。我称它们为代码单元。如果您想要文本的高级视图,这不是您要找的。这是一个适合序列化/反序列化/存储的文本视图。
它还提供了一些来自C库的工具,可以用来弥合狭窄世界和Unicode世界之间的差距:c16rtomb
/mbrtoc16
和c32rtomb
/mbrtoc32
。
<本地化库/em>
本地化库仍然认为这些"类字符对象"中的一个等于一个"字符"。这当然是愚蠢的,并且使得除了Unicode的一些小子集(如ASCII)之外的许多事情无法正常工作。
考虑一下,例如,标准在<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);
// ...
你如何期望这些函数中的任何一个能够正确地分类,比如,在u8" "
或u8"U0001F34C"
中,U+1F34C 它不可能工作,因为这些函数只接受一个代码单元作为输入。
如果您只使用char32_t
,则可以使用适当的语言环境:U'U0001F34C'
是UTF-32中的单个代码单元。
然而,这仍然意味着您只能使用toupper
和tolower
获得简单的大小写转换,例如,对于一些德语区域设置来说,这还不够好:"ß"大写为"SS";但是toupper
只能返回一个字符代码单元。
接下来是wstring_convert
/wbuffer_convert
和标准代码转换方面。
wstring_convert
用于将一种给定编码的字符串转换为另一种给定编码的字符串。这种转换涉及两种字符串类型,标准将其称为字节字符串和宽字符串。由于这些术语确实具有误导性,因此我更喜欢分别使用"序列化"answers"反序列化",而不是†
要转换的编码由作为模板类型参数传递给wstring_convert
的codecvt(代码转换facet)决定。
wbuffer_convert
执行类似的功能,但作为宽反序列化流缓冲区,封装了字节序列化流缓冲区。任何I/O都通过底层的byte序列化流缓冲区执行,并与codecvt参数给出的编码进行转换。
写入序列化到该缓冲区,然后从它写入,读取读取缓冲区,然后从它反序列化。标准提供了一些编码类模板用于这些工具:codecvt_utf8
、codecvt_utf16
、codecvt_utf8_utf16
和一些codecvt
专门化。这些标准方面一起提供了以下所有转换。(注意:在下面的列表中,左边的编码总是序列化的string/streambuf,右边的编码总是反序列化的string/streambuf;标准允许两个方向的转换)。
- UTF-8↔UCS-2 with
codecvt_utf8<char16_t>
, andcodecvt_utf8<wchar_t>
wheresizeof(wchar_t) == 2
; - UTF-8↔
codecvt_utf8<char32_t>
,codecvt<char32_t, char, mbstate_t>
,codecvt_utf8<wchar_t>
wheresizeof(wchar_t) == 4
; - UTF-16↔UCS-2 with
codecvt_utf16<char16_t>
和codecvt_utf16<wchar_t>
wheresizeof(wchar_t) == 2
; - UTF-16↔
codecvt_utf16<char32_t>
和codecvt_utf16<wchar_t>
wheresizeof(wchar_t) == 4
; - UTF-8↔
codecvt_utf8_utf16<char16_t>
,codecvt<char16_t, char, mbstate_t>
,codecvt_utf8_utf16<wchar_t>
wheresizeof(wchar_t) == 2
; - 窄↔宽↔
codecvt<wchar_t, char_t, mbstate_t>
- no-op with
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"xD83CxDF4C"
,它不能转换为UTF-8,因为UTF-8不能编码0xD800-0xDFFF范围内的任何值。
仍然在UCS-2前端,没有办法从UTF-16字节流读取到UTF-16字符串与这些方面。如果您有一个UTF-16字节的序列,则不能将其反序列化为char16_t
字符串。这是令人惊讶的,因为这或多或少是一种身份转换。然而,更令人惊讶的是,它支持使用codecvt_utf16<char16_t>
将UTF-16流反序列化为UCS-2字符串,这实际上是一种有损转换。
对UTF-16-as-bytes的支持非常好:它支持从BOM中检测尾序,或者在代码中显式地选择它。它还支持生成带有或不带有BOM的输出。
缺少一些更有趣的转换可能性。没有办法将UTF-16字节流或字符串反序列化为UTF-8字符串,因为不支持UTF-8作为反序列化形式。
这里的窄/宽世界与UTF/UCS世界是完全分开的。旧式窄/宽编码和任何Unicode编码之间没有转换。
输入/输出库
I/O库可以使用上面描述的wstring_convert
和wbuffer_convert
设施来读写Unicode编码的文本。我不认为标准库的这一部分还需要支持什么。
我以前已经阐述过c++正则表达式和Unicode在Stack Overflow上的问题。我不会在这里重复所有这些要点,但只是声明c++正则表达式不具有1级Unicode支持,这是使它们可用而不诉诸于到处使用UTF-32的最低要求。
吗?
是的,就是这样。这是现有的功能。有很多Unicode功能,像标准化或文本分割算法是看不到的。
U + 1 f4a9。有没有办法在c++中获得更好的Unicode支持?
常用的:ICU和Boost.Locale.
匕首;毫无疑问,字节串是字节串,即char
对象。然而,与宽字符串字面值不同,总是wchar_t
对象的数组,在此上下文中的"宽字符串"不一定是wchar_t
对象的字符串。事实上,标准从未明确定义"宽字符串"的含义,所以我们只能从用法中猜测其含义。由于标准术语是草率和令人困惑的,为了清晰起见,我使用我自己的术语。
像UTF-16这样的编码可以存储为char16_t
序列,然后没有端序;或者它们可以存储为字节序列,这些字节序列具有端进制(每个连续的字节对可以根据端进制表示不同的char16_t
值)。该标准支持这两种形式。char16_t
序列对于程序中的内部操作更有用。字节序列是与外部世界交换这些字符串的方式。因此,我将使用"序列化"answers"反序列化"来代替"字节"answers"宽"。
‡如果你要说"但是Windows!"抓住你的 。自Windows 2000以来的所有Windows版本都使用UTF-16。
☦是的,我知道großes Eszett(ẞ),但即使你在一夜之间改变所有德国地区ßẞ大写,还有很多其他的情况下,这将失败。试试大写的U+FB00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _没有任何一种说法是:ғғ;只是大写到两个f。或U+01F0;没有预先设定好的资本;它只是大写字母J和一个组合号。
标准库不支持Unicode(对于支持的任何合理含义)。
std::string
并不比std::vector<char>
好:它完全无视Unicode(或任何其他表示/编码),只是将其内容视为字节的blob。
如果你只需要存储和连接blobs,它工作得很好;但是一旦你想要Unicode功能(代码点的数量,字素的数量等),你就不走运了。
我所知道的唯一全面的库是ICU。c++接口是从Java接口派生出来的,所以它远不是惯用的。
您可以安全地将UTF-8存储在std::string
中(或者在char[]
或char*
中),因为Unicode NUL (U+0000)在UTF-8中是空字节,并且这是在UTF-8中出现空字节的唯一方式。因此,您的UTF-8字符串将根据所有C和c++字符串函数正确终止,并且您可以使用c++ iostreams(包括std::cout
和std::cerr
,只要您的语言环境是UTF-8)来使用它们。
对于UTF-8,std::string
不能做的是获取代码点长度。std::string::size()
将以字节为单位告诉您字符串长度,它只等于在UTF-8的ASCII子集内的代码点数。
如果你需要在代码点级别操作UTF-8字符串(即不仅仅是存储和打印它们),或者如果你正在处理UTF-16,这可能有许多内部空字节,你需要查看宽字符串类型。
c++ 11为Unicode提供了两个新的字面值字符串类型。
不幸的是,标准库中对非统一编码(如UTF-8)的支持仍然很差。例如,没有很好的方法来获取UTF-8字符串的长度(以码位为单位)。
然而,有一个非常有用的库叫做tiny-utf8,它基本上是的一个临时替代品std::string
/std::wstring
。它旨在填补仍然缺失的utf8-string容器类的空白。
这可能是'处理' utf8字符串的最舒适的方式(也就是说,没有unicode规范化和类似的东西)。您可以轻松地操作代码点,而您的字符串保持在运行长度编码的char
s中进行编码。
- 编译时未启用intel oneApi CUDA支持
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用CMake检测支持的C++标准
- 为什么istream不支持右值提取
- 当我编译webrtc服务器时,Windows上只支持clang-cl
- 扩展光电二极管探测器以支持多个传感器
- MSVC是否支持C++11样式的属性而不是__declspec
- 在使用GPU支持编译Tensorflow时,会遇到CUDA_TOOLKIT_PATH未绑定变量
- 当使用比格式支持的精度更高的精度来显示数字时,会写出什么数据
- 错误:(-210:不支持的格式或格式组合)功能'create'中的硬件视频解码器不支持视频源
- 哪个C++规范开始支持 std::vector
- 如何检测VS C++编译器是否支持C++11?
- CertGetCertificateChain 具有支持的内存存储和证书信任列表
- Casablanca/cpprestsdk listener.support接受函数,但不支持方法
- 将 IPv6 支持添加到雨量计
- 为什么C++不支持对未初始化变量进行智能分析?
- LMDB 是否支持随机读取?
- 在 c++ 中连接字符串和整数,以便在 C++ 11 不支持计算机的情况下读取多个文件
- 如何检查特定 g++ 版本支持 C++11 的程度
- Unicode在c++ 11中的支持程度