小艾的自留地

Stay foolish, Stay hungry

不得不知道的字符编码的知识。

ASCII 码

所谓ASCII 码,就是一套字符编码,它规定了英文中的各种字符在计算机里表示形式。

ASCII 码一共规定了128个字符的编码,例如字母 a 的二进制编码是0110 0001,把这个二进制转成十进制就是97。

所以字母 a 在ASCII 码表中对应的 ASCII值就是 97。

如果仔细思考一下,就会发现一个严重的问题,ASCII 码仅仅只是对英文字符进行编码。

可问题是,并不是所有国家的语言都是使用英文,于是,各个国家便有了自己的字符集。

什么是字符集?

字符集就是一个系统支持的所有抽象字符的集合,字符集说到底,它约定了不同字符在二进制上的表现形式。

常见的字符集:

  • ASCII(美国):最基本的字符集,涵盖了所有的英文字母和一些常用的字符
  • GB2312(中国):中国的字符集,加入了大概6000 个汉字
  • GBK(中国):GB2312 的扩展

可是新的问题又来了,如果放任各个国家使用自己的字符集就乱套了。

此时,如果能出现一个统一所有字符的字符集就好了,它收录了世界各国的文字,任何一个字符都可以在其中找到对应 的编码。

Unicode

于是 Unicode 便诞生了。

它的出现解决了字符集大统一的问题,涵盖了各个国家的字符集,这就是 Unicode 字符集。

那么新的问题也随之而来了。
Unicode 只是一个字符集,它规定了不同的字符在二进制上的表示形式。

比如 “中” 这个汉字,它的 Unicode 编码是 \u4e2d4e2d 是 十六进制,转换成十进制是 20013,转换成二进制是 01001110 00101101,这一个汉字,至少需要两个字节来存储。

问题就在于,Unicode 只是规定了这些字符对应的二进制值,但是并没有规定这些二进制值该如何存储。

这个汉字两个字节就能存储,但有些字符需要三个字节,甚至四个。
像 a 这种字符,以前用 ASCII 码的时候,用一个字节就能表示,但是在 Unicode 里如果采用两个字节或三个的固定长度编码,不仅空间利用率很差,同时与采用一个字节编码的 ASCII 字符集也无法兼容。

UTF-8

针对这些问题,UTF-8 编码方案便诞生了。

它是 Unicode 的一种实现,它只取了 Unicode 中最常用的一部分,通过可变长度编码方式来存储。
这样一来,既解决了Unicode 的编码问题,也尽可能地节约了空间。

对于ASCII 码表里的字符仍然用一个字节来存储,而一个汉字用三个字节来存储。

三者的关系

ASCII:一个字符集(一套字符编码),规定了 128 个字符的编码
Unicode:同样是一个字符集(一套字符编码),只不过它更大更完整,收录了世界各国的文字,任何一个字符都可以在其中找到对应 Unicode 编码
UTF-8:一种编码方案(是 Unicode 的实现),通过可变长的编码方式,解决不同字节大小的存储问题

乱码是如何产生的

在回答这个问题之前,首先来看看什么是编码和解码。

编码:将正常的字符选择任意类型的编码方式,转换为对应的二进制值,这个过程就是编码。无论使用哪一种编码方式进行编码,最终都是变成计算机的二进制值。

解码:一串二进制值,使用一种编码方式,转换成的对应的字符,这个过程称为解码。

解码时,可以使用任意的编码方式进行解码,但是往往只有一种编码方式可以显示正常,其他编码方式解出来的字符则都会是乱码。

但是这种有一个问题需要注意:
编码规范的字库表里面不包含目标字符,那就无法在字符集中找到对应的二进制值,这就会导致不可逆的乱码。

举个例子就是:ISO-8859-1 不包含中文字符,如果选择这种编码方式对中文字符进行编码,那么就算最终使用同样的编码方式进行解码,看到的始终都是乱码。

乱码说白了就是编码和解码使用的编码方式不一致导致的问题(当然,还有字库表不包含目标字符时也会出现)。

评论