UTF-8直接存储码点的原理是什么?

4
UTF-8将码点的重要位存储在码元低位中。
U+0000-U+007F       0xxxxxxx
U+0080-U+07FF       110xxxxx    10xxxxxx
U+0800-U+FFFF       1110xxxx    10xxxxxx    10xxxxxx
U+10000-U+10FFFF    11110xxx    10xxxxxx    10xxxxxx    10xxxxxx

那需要解码器检查过长的序列(例如C0 80而不是00),并且还会减少可编码到固定字节数的代码点数量。如果它使用相同的编码,但将代码点映射如下:
  • 前128个代码点(U+0000—U+007F):1个字节
  • 接下来的2048个代码点(U+0080—U+087F):2个字节。例如C0 81:U+0081
  • 接下来的65536个代码点(U+0880—U+1087F):3个字节。例如E0 B0 B1:U+0881
  • 接下来的131072个代码点(U+10880—U+10FFFF,最高到U+20880):4个字节。例如F0 B0 B0 B1:U+10881

(即该值编码为范围开始处的偏移量)

如果使用较短的序列,就可以编码更多字符。解码也可能更快,因为它只需要与常量相加,这通常比检查过长的代码点的分支成本低。实际上,如果我们从映射中删除代理对范围,就可以将2048个字符压缩到3个字节中。

那么为什么UTF-8要以这种方式存储代码点呢?

1个回答

1
这个理由在“餐垫”趣闻中有很好的阐述,它讲述了当Unicode团队(实际上是来自X/Open的某人)联系Ken Thompson和Rob Pike审查草案规范时,他们在一家餐厅里草拟了这个规范。http://doc.cat-v.org/bell_labs/utf-8_history包含了Rob Pike本人的叙述,以及他、Ken Thompson和X/Open人员之间的通信记录。其中指出了这个愿望作为早期草案中缺失的关键部分之一:即在运行中获取的字节流同步的能力,在同步前不到一个字符被消耗。换句话说,当您查看高位设置的字节时,仅从该字节值即可判断您是否处于UTF-8序列的中间位置,如果是,还需倒回多少步才能回到多字节编码字符的开头。
完整的故事值得一读,因此我在这里简要概述一下。以下是维基百科文章历史部分的一个摘录版本。
到1992年初,人们开始寻找一个好的多字节字符集的字节流编码方式。草案ISO 10646标准包含了一个非必选附录,称为UTF-1,提供了其32位代码点的字节流编码。出于性能等原因,这种编码并不令人满意,最大的问题可能是它没有明确区分ASCII和非ASCII......
1992年7月,X/Open委员会XoJIG正在寻找一种更好的编码方式。Unix System Laboratories的Dave Prosser提交了一个提案,具有更快的实现特性,并引入了改进:7位ASCII字符只表示它们自己;所有的多字节序列都只包括高位设置的字节。......
1992年8月,IBM X/Open代表将此提案传播给感兴趣的各方。贝尔实验室Plan 9操作系统组的Ken Thompson对此进行了修改,使它比以前的提案稍微不那么位密集,但关键是让它能够自我同步,使读者可以从任何地方开始并立即检测到字节序列边界。它还放弃了使用偏差的方法,而是添加了规则,只允许使用最短的可能编码;紧凑性的额外损失相对较小,但现在读者必须注意无效的编码,以避免可靠性和特别是安全性问题。Thompson的设计于1992年9月2日在新泽西州的一家餐馆的餐垫上概述,并与Rob Pike一起实现了它并更新了Plan 9的使用,然后将他们的成功回报给X/Open,后者将其作为FSS-UTF的规范接受了它。

我知道自同步的概念,但我不是在问那个。事实上,我上面提出的编码方式就像UTF-8一样是自同步的,只是在每个字节的低位中编码代码点的方式不同(使用偏移量而不是直接位)。我想知道为什么他们没有使用更好的偏移量方法。 - phuclv
2
也许你可以编辑你的问题以澄清;尽管我怀疑除非你能让Ken Thompson本人回答这个问题,否则唯一可能的答案就是纯粹的猜测。 - tripleee

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