本文正在参加「金石计划 . 瓜分6万现金大奖」
不知道你是否了解 Unicode ,它为每一个字符提供了唯一的编号。简单的说,Unicode 就是一个 Map,保存了编号与字符之间的映射。然而,我们在开发过程中接触最多的是 UTF-8,不是 Unicode 编码。有 Unicode 编码不就够了吗?为什么我们需要 UTF-8 呢?
为什么只有 Unicode 是不够的
在回答这些问题前,不妨先尝试自己为字符编码。简单起见,这里我们用十进制来为字母表编码:
a -- 1
b -- 2
c -- 3
...
z -- 26
如果用上面的编码表示 add 这个单词,结果就是 144。1 对应 a,4 对应 d,非常简单对不对😊😊。
当我们需要表示 hello 时,结果就是 85121215 。这时你发现问题了没有,85121215 这个编码结果还可以表示 heababo 、 heababae 、heablo等等 。也就是说,这个编码结果可以对应几种不同的字符串,这是不能接受的。
从图中我们可以看出,12 和 15 在解析过程中可以表示不同的字符组合,这就导致了结果的差异。我们这里还仅仅是对 26 个字母进行编码,而 Unicode 目前已编码数量超过了十几万种。看到这里,对于第一个问题,你应该已经知道为什么只有 Unicode 是不够的了。
为什么需要 UTF-8
上面说完了原因,现在看下解决方式。其实要解决这个问题非常简单,有两种思路:
- 第一种是固定位数解析:如固定两位解析,不足的补0。这时
hello的编码结果就为0805121215 - 第二种是增加标志位:如最简单的加空格,这时
hello的编码就是8 5 12 12 15
在 Unicode 中,解决方案叫 UTF(Unicode transformation format),有三种方式分别是 UTF-8、UTF-16、UTF-32。UTF-32 是第一种思路,固定 32 位解析,不足补0;UTF-8、UTF-16 则是第二种思路。目前不同的方式都有应用,比如 python3 用的是 UTF-32,Java 默认是 UTF-16,网络数据传输大部分都是 UTF-8。这就回答了第二个问题,我们使用 UTF-8 是为了解决上面解析 Unicode 出现的问题。
说了这么久的UTF-8 ,这里我们就来看看它的编码规则。UTF-8 的编码规则如下:
这里先解释下表格的意思:
xxx Byte 表示第几个Byte(每个Byte有8位),它们下面的字符串中 x 表示任意 0 或者 1,如 0xxxxxxx 可以是 00000000 , 也可以是 01000000,只要前面的“标头”是 0 就可以。这里的这里“标头”就相当于前面我们的空格,都是为了让计算机能正常的解析。
Number of Free Bits 表示最大表示的位数,Max Expressible Unicode Value 表示最大编码数,比如第一行最大位数是 7,它的最大编码就是 2 ^ 7 - 1 = 127。根据这个我们就能知道使用哪一行的格式进行编码,例如中文是 16 位长的,第 1 、2行的位数不够,因此只能用第三行。这里以 汉 这个字为例:
**汉**的 Unicode 编码为: U+6C49
**汉**对应的二进制为(这里空格是方便看才加的): 01101100 01001001
由于 汉 的 Unicode 的编码位数是 16 位的,因此选择第三行 1110xxxx 10xxxxxx 10xxxxxx(这里的空格是方便看才加的)
如上图的方式对 汉 进行编码,结果为 11100110 10110001 10001001
总结
这里我们实现了一套简单的编码,在解析过程中,我们发现只有对字符的映射是不行的。我们还需要一套编码方式,让解析的结果能够唯一。在 Unicode 中,这种编码方式叫做 UTF(Unicode transformation format),其中最有名的就是 UTF-8 。这也是我们有了Unicode,还需要UTF-8的原因。文章最后求求免费的赞吧🥺🥺。