一文弄懂字符编码,面对乱码不再抓瞎
我们都知道,计算机起源自美国。那时候的电脑普遍支持 8 bits 运算。如果从零开始编号,128 就足以将英文字母和其他字符全部索引进来。因此,7 位二进制数用作表示字符,即 ASCII 码(8 bit 里面多出来的 1 bit 留出来用于校验等目的)。
后来,计算机卖到了世界各国,混沌之初,一切无序。各国按照各自的语言,在 128 位以上的广阔天地里自由飞翔。
这样就导致了一个问题,美国人发来的英文邮件在西班牙的电脑上有乱码,根本看不懂!
光是想想,每个国家都有自己的小册子,上面记录了 128 位以上的字母(字符)表示,这要求每个计算机都要存储这些小册子,太疯狂了!
历史车轮滚滚向前,Unicode 出场了。它带着将世界上每一个字符都赋予编码的使命来了。
Unicode 的思路是:每一个字母都分配一个抽象的编号,比如'Hello' - U+0048 U+0065 U+006C U+006C U+006F.
好的,现在赋予编号完成了,可是计算机是基于二进制呀,Unicode 如何存储在计算机中呢?
先做一个简单的心算。上面 Hello 的编码,去掉 U+: 00 48 00 65 00 6C 00 6C 00 6F. 每个数字的范围 0-F,二进制需要四位,也就是半个 byte,所以 00 是一个 byte,一个字母 H 是两个 byte!
这时候,美国人跳出来表达了不满:我们的 ASCII 码用的好好的,八位就可以表达所有的字母,现在要我们用 Unicode,每个字母都要用 16 位,那文件大小生生比原来多了两倍!
于是,UTF-8 为了解决上述痛点,依旧将 128 以下的编码用于英文字母,且只用一个字节,只有 128 以上的,才用两个字节存储。这样,对于英语用户之间的文件传递,没有任何影响。
注意概念上的分类:Unicode 和 ASCII 都是字符集。而 UTF-8 是编码方式。
特性 | 字符集 (Character Set) | 编码方式 (Encoding) |
---|---|---|
核心问题 | 定义字符和代码点的映射 | 定义代码点如何存储为比特序列 |
抽象层次 | 抽象(逻辑层):字符和数字的关系 | 具体(实现层):数据如何表示 |
示例 | Unicode(U+0041 表示字母 A) | UTF-8、UTF-16、UTF-32 等 |
依赖关系 | 编码方式依赖于字符集 | 字符集需要通过编码方式来实现 |
计算机表现形式 | 提供标准编号(如 U+0041) | 提供实际存储格式(如 0x41 或 11000010) |
Unicode 只是一个抽象的操作,将任何字符转化为一个 U+字符串。任何计算机上实际显示一个字符串需要一个编码方案的支持。可以想象的 hello 字符串的 unicode 为...,然后某个编码方案 iso-xxx 中找不到这些码,于是显示?或者 �。
最后,作者特别指出一句话:
It does not make sense to have a string without knowing what encoding it uses.
参考文献: