Unicode 与 UTF-8
计算机编码发展的历史
关于计算机系统中编码的历史(例如 ASCII、GBK 这些编码等等)就不介绍了,如果感兴趣可以看看知乎上面的 这个回答。
Unicode 字符集
Unicode(Universal Multiple-Octet Coded Character Set) 是由 ISO 提出的,这是一种旨在包含目前世界上所有文化、所有字母的字符编码表,其目的是世界上任意一个字符都可以在该表中找到其所对应的编码值。Unicode 字符集兼容 ASCII 码中的 0 ~ 127 位字符,对于这些字符 Unicode 会将其 ASCII 码值放在低位,高位全部置为零。
Unicode 字符平面映射 把 Unicode 分为 17 个平面(Plane),每个平面拥有 2 ^ 16 = 65536 个代码点,即每个平面可以表示 65536 个字符。这 17 个平面的范围如下表:
| 平面 | 编码取值范围 |
|---|---|
| 0 号平面 | U+0000 - U+FFFF |
| 1 号平面 | U+10000 - U+1FFFF |
| 2 号平面 | U+20000 - U+2FFFF |
| 3 号平面 | U+30000 - U+3FFFF |
| 4 号平面 - 13 号平面 | U+40000 - U+DFFFF |
| 14 号平面 | U+E0000 - U+EFFFF |
| 15 号平面 | U+F0000 - U+FFFFF |
| 16 号平面 | U+100000 - U+10FFFF |
作为使用者,我们并不需要了解 Unicode 是如何设计并对字符进行分类的,我们只需要知道在 Unicode 中一个字符对应了一个唯一的编码就可以了,这个唯一编码叫做code point,其实就是该字符在编码表中的位置下标。
utf-8 编码
utf-8 是 Unicode 的一种变长度的编码表达方式,Unicode 字符集中有相当多的字符其编码高位都是零,这浪费了很多的空间。为了节省空间,可以使用 utf-8 编码方式对 Unicode 做进一步的编码注 1。相较于 Unicode,使用 utf-8 编码可以显著的减少字符的空间占用。
utf-8 对 Unicode 编码值小于 U+FFFF 的字符编码效率较高,而对于高于 U+FFFF 的字符值使用 utf-16 编码可能是个更好的选择,好在我们常用的字符其编码值基本上都不会超过 U+FFFF,所以一般来说 utf-8 编码方式已经足够满足我们的需求。不过事实上,假设不使用 utf-8 或者 utf-16 等编码方式,而是直接使用 Unicode 编码加上使用数据压缩算法(例如 DEFLATE)的方式,也能得到较好的空间节省效果。
我们已经知道了引入 utf-8 编码机制的目的是为了减少空间的占用,下面我们就详细了解一下 utf-8 的编码方式。如下是一个 utf-8 编码与 Unicode 编码的关系表
| 位数 | Unicode 范围 | 字节数 | 字节 1 | 字节 2 | 字节 3 | 字节 4 |
|---|---|---|---|---|---|---|
| 7 | U+0000 - U+007F | 1 | 0xxxxxxx | |||
| 11 | U+0080 - U+07FF | 2 | 110xxxxx | 10xxxxxx | ||
| 16 | U+0800 - U+FFFF | 3 | 1110xxxx | 10xxxxxx | 10xxxxxx | |
| 21 | U+10000 - U+1FFFFF | 4 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
上面只列举了 utf-8 编码在 4 个字节以内的编码方式,再多的因为很少使用没有列举出来。上表中的 x 代表了 utf-8 编码中的编码值,可以根据字符计算得到。
根据上表我们可以得到 utf-8 编码四种情况:
- 第一个 bit 为 0,则说明这个字符只占用一个字节,字符的值为后 7 个 bit
- 前三个 bit 为 110,则说明这个字符占用两个字节,字符的值为第一个字节的后 5 个 bit 加上第二个字节的后 6 个 bit
- 前四个 bit 为 1110,该字符占用三个字节,字符的值为 字节一 [4:] + 字节二 [2:] + 字节三 [2:]
- 前五个 bit 为 11110,该字符占用四个字节,字符的值为 字节一 [5:] + 字节二 [2:] + 字节三 [2:] + 字节四 [2:]
下面我们看一个实际的 utf-8 和 unicode 相互转化的例子:
1 | package main |
总结
utf-8 作为现在最广泛使用的编码方式,了解一下其工作原理还是有必要的。utf-8 离不开 unicode,utf-8 编码出现的目的是为对 unicode 的编码值进行压缩,这才是 utf-8 编码的最核心意义。
注
- 这里有点绕,Unicode 和 utf-8 都是编码方式,我们可以这样对它们作区分:
- Unicode 是一种表驱动的编码方式,即编码值是通过查表的方式获得的
- utf-8 是一种通过计算而编码的方式,我们可以通过计算来实现一个字符的 utf-8 编码与 Unicode 编码的相互转化