关于字符编码

今天看Swift看到utf-8,utf-16,unicode突然懵逼了,所以决定补补这些知识。


在objc中国看到一篇文章,介绍了NSString和Unicode,讲的十分不错。


首先什么是编码

计算机没法直接处理文本,它只和数字打交道。为了在计算机里用数字表示文本,我们指定了一个从字符到数字的映射。这个映射就叫做编码(encoding)。

Unicode起源

1987 年,来自几个大的科技公司(其中包括苹果和 NeXT)的工程师们开始合作致力于开发一种能在全世界所有书写系统中通用的字符编码系统,于 1991 年 10 月发布的 1.0.0 版本的 Unicode 标准就是这一努力的成果。

Unicode概要

简单地来说,Unicode 标准为世界上几乎所有的[^1]书写系统里所使用的每一个字符或符号定义了一个唯一的数字。这个数字叫做码点(code points),以 U+xxxx 这样的格式写成,格式里的 xxxx 代表四到六个十六进制的数。比如,U+0041(十进制是 65)这个码点代表拉丁字母表(和 ASCII 一致)里的字母 A;U+1F61B 代表名为“伸出舌头的脸”的 emoji,也就是 (顺便说一下,字符的名字也是 Unicode 标准的一部分。)。

Unicode 转换格式

字符和码点之间的映射只完成了一半工作,还需要定义另一种编码来确定码点在内存和硬盘中要如何表示。Unicode 标准为此定义了几种映射,叫做「Unicode 转换格式」(Unicode Transformation Formats,简称 UTF)。日常工作中,人们就直接把它们叫做「编码」—— 因为按照定义,如果是用 UTF 编码的,那么就要使用 Unicode,所以也就没必要明确区分这两个步骤了。


UTF-32

最清楚明了的一个 UTF 就是 UTF-32:它在每个码点上使用整 32 位。32 大于 21,因此每一个 UTF-32 值都可以直接表示对应的码点。尽管简单,UTF-32却几乎从来不在实际中使用,因为每个字符占用 4 字节太浪费空间了。


UTF-16

UTF-16 要常见得多,而且在下文我们会看到,它与我们讨论 NSString 对 Unicode 的实现息息相关。它是根据有 16 位固定长度的码元(code units)定义的。UTF-16 本身是一种长度可变的编码。基本多文种平面(BMP)中的每一个码点都直接与一个码元相映射。鉴于 BMP 几乎囊括了所有常见字符,UTF-16 一般只需要 UTF-32 一半的空间。其它平面里很少使用的码点都是用两个 16 位的码元来编码的,这两个合起来表示一个码点的码元就叫做代理对(surrogate pair)。

为了避免用 UTF-16 编码的字符串里的字节序列产生歧义,以及能使检测代理对更容易,Unicode 标准限制了 U+D800 到 U+DFFF 范围内的码点用于 UTF-16,这个范围内的码点值不能分配给任何字符。当程序在一个 UTF-16 编码的字符串里发现在这个范围内的序列时,就能立刻知道这是某个代理对的一部分。实际的编码算法很简单,维基百科上 UTF-16 的文章里有更多介绍。UTF-16 的这种设计也是为什么码点最长也只有奇怪的 21 位的原因。UTF-16 下,U+10FFFF 是能编码的最高值。

和所有多字节长度的编码系统一样,UTF-16(以及 UTF-32)还得解决字节顺序的问题。在内存里存储字符串时,大多数实现方式自然都采用自己运行平台的 CPU 的字节序(endianness);而在硬盘里存储或者通过网络传输字符串时,UTF-16 允许在字符串的开头插入一个「字节顺序标记」(Byte Order Mask,简称 BOM)。字节顺序标记是一个值为 U+FEFF 的码元,通过检查文件的头两个字节,解码器就可以识别出其字节顺序。字节顺序标记不是必须的,Unicode 标准把高字节顺序(big-endian byte order)定为默认情况。UTF-16 需要指明字节顺序,这也是为什么 UTF-16 在文件格式和网络传输方面不受欢迎的一个原因,不过微软和苹果都在自己的操作系统内部使用它。


UTF-8

UTF-8 使用一到四个[^5]字节来编码一个码点。从 0 到 127 的这些码点直接映射成 1 个字节(对于只包含这个范围字符的文本来说,这一点使得 UTF-8 和 ASCII 完全相同)。接下来的 1,920 个码点映射成 2 个字节,在 BMP 里所有剩下的码点需要 3 个字节。Unicode 的其他平面里的码点则需要 4 个字节。UTF-8 是基于 8 位的码元的,因此它并不需要关心字节顺序(不过仍有一些程序会在 UTF-8 文件里加上多余的 BOM)。


太晦涩了,待续

你可能感兴趣的:(关于字符编码)