如何在标准C++字符串中使用3字节和4字节Unicode字符?

hkB*_*sai 11 c++ string stl stdstring unicode-string

在标准C++中,我们有charwchar_t存储字符.char可以存储0x000xFF之间的值.并且wchar_t可以在0x0000和之间存储值0xFFFF.std::string使用char,因此它只能存储1个字节的字符.std::wstring使用wchar_t,因此它可以存储最多2个字节宽度的字符.这就是我对C++中字符串的了解.如果到目前为止我说错了,请纠正我.

我在维基百科上阅读了UTF-8的文章,我了解到一些Unicode字符占用的空间最多为4个字节.例如,汉字有一个Unicode代码点0x24B62,它占用内存中的3字节空间.

是否有一个STL字符串容器来处理这些字符?我正在寻找类似的东西std::string32.此外,我们有main()ASCII入口点,wmain()对于具有16位字符支持的入口点; 我们使用3和4字节Unicode支持的代码的入口点是什么?

你能加一个小例子吗?

(我的操作系统:Windows 7 x64)

bam*_*s53 20

首先,您需要更好地理解Unicode.您问题的具体答案在底部.

概念

您需要一套比介绍性编程课程中教授的非常简单的文本处理所需的更细微的概念.

  • 字节
  • 代码单元
  • 代码点
  • 抽象的人物
  • 用户感知角色

字节是内存的最小可寻址单位.今天通常是8位,能够存储多达256个不同的值.根据定义,char是一个字节.

代码单元是用于存储文本的最小固定大小的数据单元.当你真的不关心文本的内容而你只是想将它复制到某个地方或计算文本使用的内存量时,你就会关心代码单元.否则代码单元使用不多.

代码点表示字符集的不同成员.无论字符集中的"字符"是什么,它们都会被分配一个唯一的编号,每当您看到编码的特定编号时,您就会知道您正在处理的字符集的哪个成员.

抽象字符是在语言系统中具有意义的实体,并且与其表示或分配给该含义的任何代码点不同.

用户感知的角色就是他们听起来的样子; 用户在任何语言系统中都认为是一个角色.

在过去,char代表所有这些东西:a char定义为一个字节,在char*字符串中代码单元是chars,字符集很小,因此256个可表示的值char足以代表每个成员,以及语言系统支持很简单,因此字符集的成员主要代表用户想要直接使用的字符.

但是这个char代表几乎所有东西的简单系统不足以支持更复杂的系统.


遇到的第一个问题是某些语言使用的字符远远超过256个字符.因此引入了"广泛"的角色.宽字符仍然使用单一类型来表示上述概念中的四个,代码单元,代码点,抽象字符和用户感知字符.但宽字符不再是单个字节.这被认为是支持大字符集的最简单方法.

代码可能大部分是相同的,除了它将处理宽字符而不是char.

然而事实证明,许多语言系统并不那么简单.在某些系统中,不要让每个用户感知的字符必然由字符集中的单个抽象字符表示.因此,使用Unicode字符集的文本有时使用多个抽象字符表示用户感知的字符,或者使用单个抽象字符来表示多个用户感知的字符.

宽字符有另一个问题.由于它们增加了代码单元的大小,因此增加了用于每个字符的空间.如果希望处理可以用单字节代码单元充分表示的文本,但必须使用宽字符系统,则使用的内存量高于单字节代码单元的情况.因此,希望宽字符不要太宽.同时,宽字符必须足够宽,以便为字符集的每个成员提供唯一值.

Unicode目前包含大约100,000个抽象字符.这要求广泛的字符比大多数人使用的要宽.结果是一个广泛的人物系统; 其中大于一个字节的代码单元用于直接存储代码点值的结果是不合需要的.

总而言之,原来不需要区分字节,代码单元,代码点,抽象字符和用户感知字符.然而,随着时间的推移,有必要区分这些概念中的每一个.


编码

在上述之前,文本数据很容易存储.每个用户感知的字符对应于具有代码点值的抽象字符.有足够的字符,256个值很多.因此,只需将对应于所需用户感知字符的代码点编号直接存储为字节.稍后,对于宽字符,对应于用户满意字符的值直接存储为较大尺寸的整数,例如16位.

但是,由于以这种方式存储Unicode文本将使用比人们愿意花费更多的内存(每个字符三个或四个字节)Unicode'编码'存储文本不是通过直接存储代码点值,而是通过使用可逆函数来计算一些为每个代码点存储的代码单元值的数量.

例如,UTF-8编码可以采用最常用的Unicode代码点,并使用单个一字节代码单元表示它们.使用两个一字节代码单元存储不太常见的代码点.使用三个或四个代码单元存储仍然不太常见的代码点.

这意味着普通文本通常可以使用比16位宽字符方案更少的内存与UTF-8编码一起存储,但是存储的数字不一定直接对应于抽象字符的代码点值.相反,如果您需要知道存储哪些抽象字符,则必须"解码"存储的代码单元.如果您需要知道用户感知的字符,您必须进一步将抽象字符转换为用户感知字符.

有许多不同的编码,为了将使用这些编码的数据转换为抽象字符,您必须知道正确的解码方法.如果您不知道使用什么编码将代码点值转换为代码单元,则存储的值实际上毫无意义.


编码的一个重要含义是,您需要知道编码数据的特定操作是有效还是有意义.

例如,如果要获取字符串的"大小",您是在计算字节数,代码单元数,抽象字符数还是用户感知字符数?std::string::size()计算代码单位,如果您需要不同的计数,则必须使用其他方法.

作为另一个例子,如果你拆分一个编码的字符串,你需要知道你是否这样做,结果在那个编码中仍然有效,并且数据的含义并没有无意中改变.例如,您可能会在属于同一代码点的代码单元之间进行拆分,从而产生无效的编码.或者您可以在代码点之间进行拆分,这些代码点必须组合以表示用户感知的字符,从而生成用户将看作不正确的数据.

答案

今天,charwchar_t只能算代码单元.char只有一个字节的事实并不能阻止它代表占用两个,三个或四个字节的代码点.您只需char按顺序使用两个,三个或四个s.这就是UTF-8的工作方式.同样,使用两个字节wchar_t表示UTF-16的平台wchar_t在必要时只使用两个.实际值charwchar_t不单独表示Unicode代码点.它们表示编码代码点所产生的代码单元值.例如,Unicode代码点U + 0400被编码为UTF-8中的两个代码单元 - > 0xD0 0x80.类似地,Unicode代码点U + 24B62被编码为四个代码单元0xF0 0xA4 0xAD 0xA2.

因此,您可以使用std::string来保存UTF-8编码数据.

在Windows上,不仅main()支持ASCII,还支持系统char编码.不幸的是,Windows不支持UTF-8作为系统char编码与其他平台相同的方式,因此您只能使用cp1252等遗留编码或您的系统配置使用的任何编码.但是,您可以使用Win32 API调用直接访问UTF-16命令行参数,而不是使用main()s argcargv参数.见GetCommandLineW()CommandLineToArgvW.

wmain()argv参数完全支持Unicode.存储在wchar_tWindows上的16位代码单元是UTF-16代码单元.Windows API本身使用UTF-16,因此在Windows上使用它非常容易.wmain()虽然是非标准的,所以依赖于此将不可移植.