许多时候, 字符集与编码这两个概念常被混为一谈, 但两者是有差别的, 作为深入理解的第一步, 首先要明确: 字符集 与 字符集编码 是两个不同层面的概念:
- charset 是 character set 的简写, 即 字符集.
- encoding 是 charset encoding 的简写, 即 字符集编码, 简称 编码.
与接口及接口实现的对比
可以把这两者与 接口 及 接口实现 做个对比:
从这里可以很清楚地看到:
- 编码是依赖于字符集的, 就像代码中的 接口实现 依赖于 接口 一样;
- 一个字符集可以有多个编码实现, 就像一个接口可以有多个实现类一样.
具体例子及规范用法
可以简单看两个例子, 一个来自于 html 文件, 用的是 charset
:
<meta http-equiv="content-type" content="text/html;charset=utf-8">
另一个来自于 xml 文件, 用的是 encoding
:
<?xml version="1.0" encoding="UTF-8"?>
哪一种用法更规范呢? 显然是后者, 它更加准确地区分了字符集与编码的概念.
charset=utf-8
容易让人误解为存在一种叫 UTF-8 的字符集.但实际上, 无论是 UTF-8, UTF-16 还是 UTF-32 都是对同一种字符集的不同编码实现而已.
为什么要严格区分字符集与编码这两个概念?
在早期, 字符集与编码是一对一的, 但随着时间的发展, 出现了一对多的情形.
字符集与编码一对一的情形
有很多的字符编码方案, 一个字符集只有唯一一个编码实现, 两者是一一对应的. 比如 GB2312, 这种情况, 无论你怎么去称呼它们, 比如"GB2312编码", "GB2312字符集", 说来说去其实都是一个东西, 可能它本身就没有特意去做什么区分, 所以无论怎么说都不会错.
为什么一对一是一种普遍的情况呢?
我们以 GB2312 为例, GB=Guo Biao=国标=国家标准, 标准出来本来就为了统一, 你一个标准弄出 N 个编码实现来, 你让人家用哪个呢?
字符集与编码一对多的情形
事情到了 Unicode 这里, 变得不一样了, 唯一的 Unicode 字符集 对应了三种编码实现: UTF-8, UTF-16, UTF-32. 如果还是这么笼统地去称呼, 就很容易搞混了.
为什么 Unicode 这么特殊?
人们弄出新的字符集标准, 驱动力无外乎是旧的字符集里的字符不够用了. Unicode 的目标是统一所有的字符集, 囊括所有的字符, 所以字符集发展到它这里就到头了, 再去整什么新的字符集就没必要也不应该了.
但如果觉得它现有的编码方案不太好呢? 在不能弄出新的字符集情况下, 只能在编码方面做文章了, 于是就有了多个实现, 这样一来传统的一一对应关系就打破了.
我们严格地区分字符集与编码两个概念, 理由就在这里.
指定了编码, 它所对应的字符集自然就指定了, 编码才是我们最终要关心的.
Unicode 早期与现在的对比
让我们来看一个图, 它展现了 Unicode 早期与现在的一些差别:
注: 由于历史方面的原因, 你还会在不少地方看到把 Unicode 和 UTF-8 混在一块的情况, 这种情况下的 Unicode 通常就是 UTF-16 或者是更早的 UCS-2 编码, 在后面的篇章中我们会进一步分析.
下面是"记事本"程序保存时的一个截图, 是 Unicode 的一个不规范使用, 这里的 Unicode 就是指 UTF-16:
我们现在说了不少 Unicode, 由于各种原因, 必须承认, 在不同的语境下, Unicode 这个词有着不同的含义, 它可能指:
- Unicode 标准
- Unicode 字符集
- Unicode 的抽象编码(编号), 也即 码点(code point).
- Unicode 的一个具体编码实现, 通常即为 变长 的 UTF-16(16 或 32 位), 又或者是更早期的定长 16 位的 UCS-2.
关于这些话题在后面的篇章里会做进一步探讨.