before_unicode

打算写几篇 Blog 把之前学习的 Unicode 的一些东西整理下。

抽象字符表

这是一个大家了解比较少的概念。简单的说,抽象字符表是「所有抽象字符的集合」

[image:101F4772-FD64-4E5E-9A4D-ED3C583650A3-297-0000F245656331E5/6D3A3F9F-1349-4F83-B326-B8B952060BE2.png]

这个字符表可以是封闭的,比如上图的 ASCII,声明有限的 128 个字符,且不允许新字符添加进去。

字符表也可以是开放的,比如 Unicode 字符表,允许不断地添加字符来拓展字符表。

编码字符集

也许你已经被 Unicode, UTF-8, GBKGBK 扰乱了头脑,现在可以先抛开这些概念,了解一下什么是「编码字符集」

在上图中,每个「字符」都对应一个值,这个值叫做「码位值(Code Point Value)」,比如在「ASCII 编码字符集」中,字符「A」的码位值是「十进制数 65」

当我们构建了「抽象字符表的字符」与「码位值」的映射关系时,也就构建了一个编码字符集。

一些常见字符集

  • ASCII:美国信息互换标准编码字符集
  • GB 2312:中华人民共和国国家标准简体中文字符集
  • Big 5:台湾地区使用的中文字符集
  • UCS:通用字符集

其中 GB 2312 和 Big5 都属于 DBCS (两字节字符集),即这个字符集的空间大小是 2 字节。

编码

在我们之前聊的字符集,更多的是逻辑上的映射(比如 A 对应的是 65),但是当我们想要在计算机使用时,我们要解决的一个问题就是如何「存储与传输一个字符」,这就引入了「编码/解码」这个概念

ASCII 的编解码很简单,就是占用一个 Byte 的定长空间,存储「码位值」的二进制数(比如 A 的码位值是 65,二进制数为 01000001)

GB 2312 的编码
[image:481859E9-52E0-41DD-8E51-2FFF4A4136C5-297-0000F64D7515AC9C/ECA8A5DD-AD0B-414D-8E67-5834239BAEF0.png]

  • 为了兼容 ASCII,如果一个字符是 ASCII 字符集中的字符,则采用 ASCII 字符集的编码形式,如字符「A」还是转换为「0x41」进行存储
  • 若是 GB2312 字符集中独有的字符,比如「哦」,对应的码位值为「0xC5B6」,则会存储为两个字节

GB2312 的解码

[image:75118E6C-F31D-481B-A90D-F4DFF0677B53-297-0000F6F5721461D1/E95A7FD6-9490-4319-8E4E-D8536E2B960E.png]

读取一个 Byte,如果值小于 128,在是 ASCII 中的字符。如果大于等于 128,就再读取一个字节,两个字节的值算出「码位值」。

为什么会有这么多字符集,我们又为什么需要 UCS

也许是因为我们的短视?

一开始大家觉得 ASCII 就够用,但是显然不是全世界都在说英语。

于是大家就开始搞自己的字符集。欧洲人搞了 ASCII 扩展字符集来支持拉丁字母,大陆搞了 GB 2312 支持很多汉字。台湾搞了「Big 5」来支持汉字。

不过这样搞完之后,我们遇到了新的问题。

[image:72C80279-B72D-4C2D-8789-BC647AAC4592-297-0000F7CEE660905C/061EE11E-334C-4879-96F0-1EFC2DCF06D3.png]

同样一个「鄙」字,台湾厂商会按照 Big5 的字符集与编码规则存成「0xBBC0」,大陆用户按照 GB2312 读出来会转换成「焕」。

对于「發」字,台湾按照 Big5 的字符集与编码规则存成「0xB560」,大陆这边读出来之后发现 GB2312 里这个值找不到对应的字,于是只好显示成「?」了。

这也是为什么早起很多 PC 用户装台湾的盗版游戏会普遍出现乱码,需要借助「金山快译内码转换器」这种工具的原因,这种工具本质就是帮你把「Big 5 的值转为对应的繁体字,再找到对应的简体字,再转为 GB2312 的值」