UTF-8如何实现"可变字节长度编码"?

128

Unicode标准包含足够的代码点,需要4个字节才能存储所有这些代码点。这就是UTF-32编码的作用。然而,UTF-8编码通过使用称为“可变宽度编码”的东西,将它们压缩到更小的空间中。

事实上,它设法仅使用一个字节来表示美国 ASCII 的前127个字符,看起来与真正的ASCII完全相同,因此你可以将大量的ASCII文本解释为UTF-8格式,而无需对其做任何处理。非常巧妙的技巧,那么它是如何工作的呢?

我将在此提出并回答自己的问题,因为我刚刚读了一些内容来弄清楚它是如何工作的,我认为这可能会为其他人节省一些时间。如果我犯了一些错误,也许还有人可以纠正我。


8
直接使用 Unicode 并不需要32位来编码其所有的码点。它曾经声称有那么多可能的码点,但在 UTF-8 流行后,Unicode 故意将自己限制为21位,以便 UTF-8 每个字符永远不会超过4个字节。目前,Unicode 只需要17位即可容纳所有可能的码点。如果没有这个限制,UTF-8 可能会达到每个字符6个字节。 - Warren Young
@Warren:大部分是准确的,但Unicode是一个21位编码(U+0000到U+10FFFF)。 - Jonathan Leffler
3
@Warren:4字节限制的UTF-8可以支持高达U+1FFFFF的字符。将其限制为U+10FFFF是为了配合UTF-16。 - dan04
@dan04,UTF-16如何受限于U+10FFFF有没有简单的解释?更多了解这个会很好。 - A-letubby
@A-letubby:由于UTF-16的“代理项”代码的分配使得有1024个前导代理项和1024个尾随代理项(它们只能成对使用),可以在BMP之外提供约100万个附加字符,以使0x110000个字符成为可能,加上BMP中可用的2^16个字符。 - dan04
@A-letubby:UTF-16使用的编码方案无法物理编码U+10FFFF以上的代码点,但UTF-8可以(早期的UTF-8规范允许使用5字节和6字节序列来处理高达U+7FFFFFFF的代码点)。RFC 3629将UTF-8中最高合法代码点限制为U+10FFFF,以保持与UTF-16的兼容性,根据第12节从RFC 2279的更改:“将字符范围限制为0000-10FFFF(UTF-16可访问范围)”。 - Remy Lebeau
3个回答

145
每个字节开头都有一些位,告诉您它是单字节代码点、多字节代码点还是多字节代码点的继续。就像这样:
0xxx xxxx    A single-byte US-ASCII code (from the first 127 characters)

多字节编码点的每个起始位都包含了一些信息,它们告诉你需要读取下一个字节(或两个、三个字节)来确定我是什么。具体而言,这些信息包括:

110x xxxx    One more byte follows
1110 xxxx    Two more bytes follow
1111 0xxx    Three more bytes follow

最终,跟随这些起始码的字节都是这个样子:

10xx xxxx    A continuation of one of the multi-byte characters

由于你可以从前几个比特位确定正在查看的字节类型,因此即使某些位置出现了问题,你也不会失去整个序列。


15
故事并不止于此——因为编码必须是字符的最短编码,这意味着例如0xC0和0xC1字节不能出现在UTF-8中;实际上,0xF5..0xFF也不能出现。请参阅http://unicode.org/faq/utf_bom.html或http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf的UTF-8常见问题解答。 - Jonathan Leffler
3
为什么不能只用一个字节来表示“下一个字符是续接”的信息呢?如果我们遇到了一个由3个字节组成的字符,那么它应该是这样的形式:1xxxxxxx 1xxxxxxx 0xxxxxxx,这样就可以节省空间。 - user5147563
12
UTF-8 被称为“自同步”编码,这意味着如果由于错误导致序列的某些部分丢失,可以检测到并丢弃损坏的部分。如果你读取以10xx开头的字节,并且没有前面的“起始”字节,那么可以将其丢弃,因为它是无意义的。如果你使用了像你所描述的系统,并且丢失了其中一个起始字节,你可能会得到一个不同的有效字符,而没有任何错误的指示。此外,它还能够轻松地定位下一个有效字符,并校正缺失的“连续”字节。 - htmlcoderexe

10

RFC3629 - UTF-8, 一种基于ISO 10646的转换格式 是这里的最终权威,并提供了所有解释。

简单地说,UTF-8编码中每个字节的几位用来表明它是一个尾部字节还是一个首部字节,如果是首部字节,后面有多少个字节也要进行标记。其余的比特位包含有效载荷(即字符的实际内容)。


1
呃,我有点傻,我以为Unicode标准是UTF-8的最终权威。 - John Machin
6
Unicode标准定义了Unicode本身,但它并未定义用于编码Unicode文本以进行各种目的(例如存储和传输)的各种方法,包括今天和将来的方法。 UTF-8是其中之一,上述参考文献是定义UTF-8的文件。 - azheglov
2
RFC3629第3页第3节说:“UTF-8由Unicode标准定义”。 - John Machin
在unicode.org上追寻链接,我找到了《Unicode标准》的第3.9节,特别是D92定义(以及次要的D86)。我不知道这个链接在新版本发布时会有多大用处,但我想他们希望跨版本保持章节和定义标识符的稳定性。 - tripleee

4

这是一篇不错的文章,但 Joel 关于字符串最大长度的说法似乎是错误的;维基百科页面显示每个字符只有1到4个字节。 - unwind
4
正如我之前所说的,当UTF-8首次被创建时,Unicode为代码点声称拥有最多32位,不是因为他们真的需要它,而只是因为32位是一个方便的值,而且他们已经超过了16位字符的先前限制。在UTF-8被证明很受欢迎后,他们选择永久限制最大代码点数为2^21,这是你可以用UTF-8方案的4个字节编码的最大值。Unicode中仍然少于2^17个字符,因此我们可以通过这种新方案将Unicode中的字符数量增加到四倍以上。 - Warren Young
好的,但不是 OP 要求的解释。 - Nishant
3
这并没有回答问题。 - Koray Tugay

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