为什么UTF-8编码的Unicode代码点不能适应3个字节?

3

维基百科

Unicode 包括了从0hex到10FFFFhex范围内的1,114,112个码位。

我有点困惑,为什么 Unicode 编码可以使用多达4个字节。1114112个码位不可以舒适地放在3个字节中吗?也许我错过了一些需要4个字节的特殊情况,请举出具体的例子吗?


2
你已经读过关于UTF-8编码历史的维基百科文章了吗?那应该能解答很多问题。 - Roland Illig
我确实读过它,但可能在完全理解它方面存在一些差距,或者可能是我想得太多了。我猜测使用1-4字节进行代码点编码更像是一种规则,而不是实际需要来适应当前Unicode代码点的限制为21位。我猜他们使用32位而不是24位,是为了为未来留出空间。 - Saturday Sherpa
可能是为什么没有UTF-24?的重复问题。 - phuclv
Unicode不是一种编码。为Unicode代码点指定大小没有意义。Unicode是代码点和语义名称(例如“LATIN CAPITAL LETTER A”)之间的映射。您可以自由选择自己的编码方式。 - Giacomo Catenazzi
你可以发明一种自己的Unicode编码,因为代码点范围小于21位。你甚至可以发明一种由一个到三个8位代码单元组成的编码。UTF-8不能这样做;它已经被发明了,不需要再这样做。那么,问题是什么呢? - Tom Blodget
3个回答

5
维基百科关于UTF-8历史的文章称,早期版本的UTF-8允许编码超过21位。这些编码需要5个甚至6个字节。
之后,人们认为2^21个代码点可能足够人类剩余时间使用(与5位、6位、7位、8位和16位的思路相同),因此禁止了5个和6个字节的编码。为保持向后兼容性,所有其他编码规则均被保留。
由此,Unicode代码点的数字空间现在是0..10FFFF,甚至比21位还少一点。因此,值得检查这些21位是否适合3个字节的24位,而不是当前的4个字节。
UTF-8的一个重要特性是,每个多字节编码中的字节都有最高位设置。为了区分前导字节和尾随字节,前导字节具有第二高位设置,而尾随字节具有第二高位清除。这个特性确保了一致的排序。因此,字符可以这样编码:
0xxx_xxxx                        7 bits freely chooseable
110x_xxxx 10xx_xxxx             11 bits freely chooseable
1110_xxxx 10xx_xxxx 10xx_xxxx   16 bits freely chooseable

现在7 + 11 + 16位 = 16.04位,比需要的21位短得多。因此,按照当前的UTF-8编码规则,使用最多3个字节对所有Unicode代码点进行编码是不可能的。
您可以定义另一种编码方式,其中每个字节的最高位是连续位:
0xxx_xxxx                        7 bits freely chooseable
1xxx_xxxx 0xxx_xxxx             14 bits freely chooseable
1xxx_xxxx 1xxx_xxxx 0xxx_xxxx   21 bits freely chooseable

现在你有足够的空间来编码所有的21位代码点。但这是一个全新的编码方式,因此你需要在全球范围内建立它。根据Unicode的经验,这将需要大约20年时间。祝你好运。


感谢您用好的例子填补了我所遗漏的部分。由于多字节编码中使用的位标志占据了24位中的7位,似乎不可能用3个字节来编码所有的Unicode码点。 - Saturday Sherpa
这是错误的。两字节编码是110x_xxxx 10xx_xxxx(11位),其余部分也存在类似问题。正确的四字节编码(未显示)是1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx,共包含21位,这是写入定义的Unicode最大值U+10FFFF所需的最小位数。 - Mark Tolonen
@Mark,谢谢你的纠正,我简单地忘记了0位。我没有故意展示4字节形式,因为问题是是否已经足够3字节。 - Roland Illig
1
请注意,Unicode和UTF-8的人为限制为0x10FFFF,并不是因为"2^21个码点可能足够人类剩余时间"(谁能绝对确定呢?),而是因为这是UTF-16可以物理编码的最高码点,而且Unicode社区不希望在可预见的未来破坏与之的兼容性。这并不意味着Unicode本身不能最终超过0x10FFFF,但如果那一天真的到来,那么很有可能会创建新的编码来处理超过21位的码点。 - Remy Lebeau
@Remy 谢谢,我不知道这个细节。也许我应该经常从书上翻阅一下我的Unicode相关的书籍,以免在写代码之前先读一读。 :) - Roland Illig

2
"unicode" 不是一种编码方式。Unicode 的常用编码方式有 UTF-8、UTF-16 和 UTF-32。UTF-8 使用 1、2、3 或 4 个字节序列,下面将对其进行解释。对于一个 21 位值,需要使用前导/尾随比特序列的开销才能使用 4 个字节。
UTF-8 编码使用以下比特模式来使用最多 4 个字节来表示 Unicode 码点:
1 字节的 UTF-8 = 0xxxxxxx二进制 = 7 位 = U+0000 到 U+007F 2 字节的 UTF-8 = 110xxxxx 10xxxxxx二进制 = 11 位 = U+0080 到 U+07FF 3 字节的 UTF-8 = 1110xxxx 10xxxxxx 10xxxxxx二进制 = 16 位 = U+0800 到 U+FFFF 4 字节的 UTF-8 = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx二进制 = 21 位 = U+10000 到 U+10FFFF
UTF-8的优点在于引导字节是独特的模式,尾随字节也是独特的模式,可以轻松验证正确的UTF-8序列。还要注意,对于适合于较小序列的Unicode值使用更长的编码是非法的。例如:1100_0001 1000_0001binC1 81hex编码U+0041,但0100_0001bin41hex)是更短的序列。参考:https://en.wikipedia.org/wiki/UTF-8

UTF-8的优势是... - 更重要的优势是UTF-8与ASCII的向后兼容性,这正是推动首尾字节的位设计的原因。 - Remy Lebeau

1
我扩展了我的评论。
Unicode不是一种编码方式。为Unicode代码点设置大小是没有意义的。Unicode是代码点和语义名称之间的映射(例如“LATIN CAPITAL LETTER A”)。您可以自由选择自己的编码方式。
最初,Unicode希望成为适合16位的通用编码(以便统一日本/中国)。但如您所见,它未能达到这个目标。另一个非常重要的问题是能够在转换到Unicode时不丢失数据(这简化了转换到Unicode的过程:一次一个工具,在任何层面上)。
因此,存在一个问题,即如何将Unicode扩展以支持超过16位,但同时不破坏所有Unicode程序。想法是使用代理项,因此仅知道16位Unicode(UCS-2)的程序仍然可以工作(顺便说一下,Python2和JavaScript仅知道UCS-2,它们仍然很好地工作。语言不需要知道Unicode代码点可能超过16位)。
代理项给出了实际Unicode的上限(因此不等于2的幂)。
后来它被设计成UTF-8。特点(按设计):与ASCII兼容(在7位字符上),编码所有代码点(也包括> 16位),并能够快速到达随机位置并同步字符将开始的位置。这最后一点需要一些地址空间,因此文本不像可以那样密集,但它更实用(并且快速“滚动”文件)。这些额外的数据(用于同步)使得无法用UTF-8对所有新的Unicode代码点进行编码为3个字节。

您可以使用UTF-24(请参见注释),但您将失去UFT-8与ASCII兼容的优势,但是使用UTF-16,您通常仅使用2个字节(而不是4个字节)。

记住:超过16位的Unicode代码点很少见:古代语言,现有字形的更好表示(语义),或新表情符号(希望我们不会只用表情符号填满整个长文本)。因此,3个字节的效用还不是必需的(尚未)。也许如果外星人来到地球,我们应该用他们的新语言字符写作,我们将主要使用超过16位的Unicode代码点。我认为这不会很快发生。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接