在标准C ++中,我们有char
和wchar_t
来存储字符。 char
可以存储 0x00 和 0xFF 之间的值。 wchar_t
可以在0x0000
和0xFFFF
之间存储值。 std::string
使用char
,因此它只能存储1个字节的字符。 std::wstring
使用wchar_t
,因此它可以存储最多2个字节宽度的字符。这就是我对C ++中字符串的了解。如果我在这一点上说错了,请纠正我。
我在维基百科中阅读the article for UTF-8,我了解到一些Unicode字符占用的空间最多为4个字节。例如,中文字符 的Unicode代码点 0x24B62 ,在内存中占用3个字节的空间。
是否有用于处理这类字符的STL字符串容器?我正在寻找像std::string32
这样的东西。此外,对于ASCII入口点我们有main()
,对于具有16位字符支持的入口点我们wmain()
;我们使用什么入口点来支持3字节和4字节的Unicode? p>
你能加一个小例子吗?
(我的操作系统:Windows 7 x64)
答案 0 :(得分:20)
首先,您需要更好地理解Unicode。您问题的具体答案在底部。
您需要一套比介绍性编程课程中教授的非常简单的文本处理所需的更细微的概念。
字节是内存的最小可寻址单位。今天通常是8位,能够存储多达256个不同的值。根据定义,char是一个字节。
代码单元是用于存储文本的最小固定大小的数据单位。如果您并不真正关心文本内容,而只是想将其复制到某处或计算文本使用的内存量,那么您就会关心代码单元。否则代码单元没有多大用处。
代码点表示字符集的不同成员。无论什么'字符'在字符集中,它们都被分配了一个唯一的编号,每当您看到编码的特定数字时,您就会知道您正在处理的字符集的哪个成员。
抽象字符是在语言系统中具有意义的实体,与其表示或分配给该含义的任何代码点不同。
用户感知的角色就是他们听起来的样子;用户在他所使用的任何语言系统中所认为的角色。
在过去,char
代表了所有这些:根据定义char
为字节,char*
字符串代码单位为char
s,字符集很小,因此char
可表示的256个值足以代表每个成员,并且支持的语言系统很简单,因此字符集的成员主要代表用户想要直接使用的字符。 / p>
但这个简单的系统char
几乎代表了一切,并不足以支持更复杂的系统。
遇到的第一个问题是某些语言使用的字符远远超过256个字符。所以'宽'人物介绍。宽字符仍然使用单一类型来表示上述概念中的四个,代码单元,代码点,抽象字符和用户感知字符。但宽字符不再是单个字节。这被认为是支持大字符集的最简单方法。
代码可能大致相同,只是它会处理宽字符而不是char
。
然而事实证明,许多语言系统并不那么简单。在某些系统中,不要让每个用户感知的字符必然由字符集中的单个抽象字符表示。因此,使用Unicode字符集的文本有时会使用多个抽象字符表示用户感知的字符,或者使用单个抽象字符来表示多个用户感知的字符。
宽字符有另一个问题。由于它们增加了代码单元的大小,因此增加了用于每个字符的空间。如果希望处理可以用单字节代码单元充分表示的文本,但必须使用宽字符系统,则使用的内存量高于单字节代码单元的情况。因此,希望宽字符不要太宽。同时,宽字符必须足够宽,以便为字符集的每个成员提供唯一值。
Unicode目前包含大约100,000个抽象字符。这要求广泛的字符比大多数人使用的要宽。结果是一个广泛的人物系统;大于一个字节的代码单元用于直接存储代码点值的结果是不合需要的。
总而言之,最初没有必要区分字节,代码单元,代码点,抽象字符和用户感知字符。然而,随着时间的推移,有必要区分这些概念。
在上述之前,文本数据很容易存储。每个用户感知的字符对应于具有代码点值的抽象字符。有足够的字符,256个值很多。因此,只需将对应于所需用户感知字符的代码点编号直接存储为字节。稍后,对于宽字符,与用户感知字符对应的值将直接存储为较大尺寸的整数,例如16位。
但是,由于以这种方式存储Unicode文本会占用比人们愿意花费更多的内存(每个字符需要三到四个字节)Unicode'编码'存储文本不是通过直接存储代码点值,而是通过使用可逆函数来计算为每个代码点存储的一些代码单元值。
例如,UTF-8编码可以使用最常用的Unicode代码点,并使用单个一字节代码单元表示它们。使用两个一字节代码单元存储不太常见的代码点。使用三个或四个代码单元存储仍然不太常见的代码点。
这意味着普通文本通常可以使用UTF-8编码以比16位宽字符方案更少的内存存储,但是存储的数字不一定直接对应于抽象字符的代码点值。相反,如果您需要知道存储哪些抽象字符,您必须解码'存储的代码单元。如果您需要知道用户感知的字符,您必须进一步将抽象字符转换为用户感知字符。
有许多不同的编码,为了将使用这些编码的数据转换为抽象字符,您必须知道正确的解码方法。如果您不知道使用什么编码将代码点值转换为代码单元,那么存储的值实际上毫无意义。
编码的一个重要含义是您需要知道编码数据的特定操作是有效还是有意义。
例如,如果你想获得'尺寸'字符串是你计算字节,代码单位,抽象字符或用户感知字符? std::string::size()
计算代码单位,如果您需要不同的计数,则必须使用其他方法。
另一个例子是,如果你拆分一个编码的字符串,你需要知道你是否这样做是为了使结果在该编码中仍然有效,并且数据的含义还没有&#39 ; t无意中改变了。例如,您可能会在属于同一代码点的代码单元之间进行拆分,从而产生无效的编码。或者您可以在代码点之间进行拆分,这些代码点必须组合以表示用户感知的字符,从而生成用户将看作不正确的数据。
今天char
和wchar_t
只能被视为代码单元。 char
只有一个字节这一事实并不能阻止它代表占用两个,三个或四个字节的代码点。您只需按顺序使用两个,三个或四个char
。这就是UTF-8的工作方式。同样,使用两个字节wchar_t
表示UTF-16的平台在必要时只需连续使用两个wchar_t
。 char
和wchar_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 argc
和argv
参数。请参阅GetCommandLineW()
和CommandLineToArgvW
。
wmain()
' argv
参数完全支持Unicode。存储在Windows上的wchar_t
中的16位代码单元是UTF-16代码单元。 Windows API本机使用UTF-16,因此在Windows上使用它非常容易。 wmain()
虽然不合标准,但依赖于此并不是便携式的。
答案 1 :(得分:4)
wchar_t
的大小和含义是实现定义的。在Windows上它是16位,就像你说的那样,在类Unix系统上,它通常是32位但不总是。
就此而言,允许编译器做自己的事情并为wchar_t
选择不同于系统所说的大小 - 它不会与系统的其余部分ABI兼容。 / p>
C ++ 11提供std::u32string
,用于表示unicode代码点的字符串。我相信最近的微软编译器包含它。它的使用有限,因为微软的系统功能需要16位宽的字符(a.k.a UTF-16le),而不是32位的unicode代码点(a.k.a UTF-32,UCS-4)。
您提到UTF-8:UTF-8编码数据可以存储在常规std::string
中。当然因为它是一个可变长度编码,你不能通过索引访问unicode代码点,你只能通过索引访问字节。但是,即使使用u32string
,您通常也不需要按索引编写代码来编写代码点。由于Unicode中存在组合标记,因此Unicode代码点与可打印字符(“字形”)不对应1-1,因此在学习编程时使用字符串的许多小技巧(反转它们,搜索子字符串)无论你将它存储在什么内容,都不能轻易使用Unicode数据。
正如你所说,这个角色是\ u24B62。它是UTF-8编码为一系列四个字节,而不是三个:F0 A4 AD A2。在UTF-8编码数据和unicode代码点之间进行转换是一种努力(诚然,这不是一项巨大的努力,而且库函数会为您完成)。最好将“编码数据”和“unicode数据”视为单独的事物。您可以使用您认为最方便的任何表示,直到您需要的位置(例如)将文本呈现为屏幕。此时,您需要(重新)将其编码为输出目标理解的编码。
答案 2 :(得分:3)
Windows使用UTF-16。 U + 0000至U + D7FF和U + E000至U + FFFF范围内的任何代码点将直接存储;根据UTF-16编码规则,这些范围之外的任何值将被分成两个16位值。
例如,0x24B62将被编码为0xd892,0xdf62。
您可以按照自己喜欢的方式转换字符串以使用它们,但Windows API仍然需要并提供UTF-16,这样可能是最方便的。
答案 3 :(得分:2)
在标准C ++中,我们有char和wchar_t来存储字符? char可以存储0x00和0xFF之间的值。并且wchar_t可以存储0x0000和0xFFFF之间的值
不完全:
sizeof(char) == 1 so 1 byte per character.
sizeof(wchar_t) == ? Depends on your system
(for unix usually 4 for Windows usually 2).
Unicode字符最多占用4个字节的空间。
不完全。 Unicode不是编码。 Unicode是定义每个代码点的标准,代码点限制为21位。前16位定义了代码平面上的字符位置,而后面的5位定义了字符所在的平面。
有几种unicode 编码(UTF-8,UTF-16和UTF-32是最常见的)这就是将字符存储在内存中的方式。这三者之间存在实际差异。
UTF-8: Great for storage and transport (as it is compact) Bad because it is variable length UTF-16: Horrible in nearly all regards It is always large and it is variable length (anything not on the BMP needs to be encoded as surrogate pairs) UTF-32: Great for in memory representations as it is fixed size Bad because it takes 4 bytes for each character which is usually overkill
我个人使用UTF-8进行传输和存储,使用UTF-32进行文本内存表示。
答案 4 :(得分:1)
char
和wchar_t
不是用于文本字符串的唯一数据类型。 C ++ 11引入了新的char16_t
和char32_t
数据类型以及std::u16string
的{{1}}和std::u32string
类型定义,以解决{{1}的ambiquity问题。类型,在不同平台上具有不同的大小和编码。 std::basic_string
在某些平台上为16位,适用于UTF-16编码,但在其他平台上为32位,适用于UTF-32编码。所有平台上wchar_t
特别是16位和UTF-16,wchar_t
特别是32位和UTF-32。