字符集和字符编码

前言

对于计算机来说,它无法处理文本,只能与0,1数字打交道。为了可以在计算机中用数字表示文本,我们指定了一个从字符到数字的映射。这个映射叫做编码(encoding)。反之,我们将计算机中数字解析显示为字符,成为解码(decoding)。

字符集(Charset)

是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

*

字符编码(Character Encoding)

是一套法则,将符号集合转换为计算机可以理解的数字。


字符集

  • 常见的字符集

ASCII
GB2312
GB18030
BIG5
Unicode

ASCII

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646

ASCII 码是 7 位的,它将英文字母,数字 0-9 以及一些标点符号和控制字符映射为 0-127 这些整型。

ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而EASCII虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的苹果电脑已经抛弃ASCII而转用Unicode

GB2312

GB 2312 或 GB 2312–80 是中华人民共和国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,通常簡稱GB,又稱GB0,由中国国家标准总局发布,1981年5月1日实施。GB 2312编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。

GB 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

GB 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁體字,GB 2312不能处理,因此后来GBKGB 18030汉字字符集相继出现以解決這些問題。

GB18030

GB 18030,全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030共收录汉字70,244个。

  • GB 18030主要有以下特点

采用变长多字节编码,每个字可以由1个、2个或4个字节组成。
编码空间庞大,最多可定义161万个字元。
完全支持Unicode,无需动用造字区即可支持中国国內少数民族文字、中日韩和繁体汉字以及emoji等字符。

BIG5

Big5,又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录13,060个汉字。

中文码分为内码及交换码两类,Big5属中文内码,知名的中文交换码有CCCII、CNS11643。Big5虽普及于台湾、香港与澳门等繁体中文通行区,但长期以来并非当地的国家标准,而只是业界标准。倚天中文系统、Windows等主要系统的字符集都是以Big5为基准,但厂商又各自增加不同的造字与造字区,派生成多种不同版本。

2003年,Big5被收录到CNS11643中文标准交换码的附录当中,取得了较正式的地位。这个最新版本被称为Big5-2003。

不同国家、不同语言在网络上交流时,如果都实现类似GB2312/GB18030/BIG5的编码方案,各自出一套方案,相互不兼容,就非常容易出现乱码。

Unicode

为了解决这个问题,Unicode(统一码、万国码、单一码、标准万国码)应运而生。Unicode为世界上几乎所有的书写系统里所使用的每一个字符或符号定义了一个唯一的数字,这个数字叫做码点(code points)。它使用4字节的数字来表达每个字母、符号,或者表意文字(ideograph)。

最初,Unicode 编码是被设计为 16 位的,提供了 65,536 个字符的空间。当时人们认为这已经大到足够编码世界上现代文本里所有的文字和字符了。

后来,考虑到要编码历史上的文字以及一些很少使用的日本汉字和中国汉字,Unicode 编码扩展到了 21 位(从 U+0000 到 U+10FFFF)。Unicode 不是 16 位的编码!它是 21 位的。这 21 位提供了 1,114,112 个码点,其中,只有大概 10% 正在使用,所以还有相当大的扩充空间。

简单来说:Unicode是一个统一了地球上所有语言的字符集。

编码

字符和码点之间的映射已经有了,但还需要定义另一种编码来确定码点在内存和硬盘中要如何表示。

Unicode 标准为此定义了几种映射,叫做「Unicode 转换格式」(Unicode Transformation Formats,简称 UTF)。

日常工作中,人们就直接把它们叫做「编码」—— 因为按照定义,如果是用 UTF 编码的,那么就要使用 Unicode,所以也就没必要明确区分这两个步骤了。

UTF-32

最清楚明了的一个 UTF 就是 UTF-32:它在每个码点上使用整 32 位。32 大于 21,因此每一个 UTF-32 值都可以直接表示对应的码点。

尽管简单,UTF-32却几乎从来不在实际中使用,因为每个字符占用 4 字节太浪费空间了。比如:表述一个字母A(只需要7位一个字节),前3字节空间被浪费了。

UTF-16

尽管有Unicode字符非常多,但是实际上大多数人不会用到超过前65535个以外的字符。因此,就有了另外一种Unicode编码方式,叫做UTF-16。

UTF-16 本身是一种长度可变的编码。基本多文种平面(BMP)中的每一个码点都直接与一个码元相映射。鉴于 BMP 几乎囊括了所有常见字符,UTF-16 一般只需要 UTF-32 一半的空间。其它平面里很少使用的码点都是用两个 16 位的码元来编码的,这两个合起来表示一个码点的码元就叫做代理对(surrogate pair)。

和所有多字节长度的编码系统一样,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-16 还是在常用的英文和西欧文本上浪费了大量的空间:每个 16 位的码点的高 8 位的值都会是 0。

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。

UTF-8使用一至四个字节为每个字符编码:

  • 128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
  • 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码(Unicode范围由U+0080至U+07FF)。
  • 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码。
  • 其他极少使用的Unicode辅助平面的字符使用四字节编码。

iOS相关

NSString和Unicode

NSString文档

An NSString object encodes a Unicode-compliant text string, represented as a sequence of UTF–16 code units. All lengths, character indexes, and ranges are expressed in terms of UTF–16 code units, with index values starting at 0. The length property of an NSString returns the number of UTF-16 code units in an NSString, and the characterAtIndex: method retrieves a specific UTF-16 code unit. These two “primitive” methods provide basic access to the contents of a string object.

我们已经了解了 Unicode 是一种 21 位的编码方案。

就想文档所说,NSString 代表的是用 UTF-16 编码的文本,长度、索引和范围都基于 UTF-16 的码元。除非你知道字符串的内容,或者你提前有所防范,不然 NSString 类里的方法都是基于上述概念的,无法给你提供可靠的信息。每当文档提及「字符」(character)或者 unichar 时,它其实都说的是码元。

NSString 对象代表的是用 UTF-16 编码的码元组成的数组。相应地,length 方法的返回值也是字符串包含的码元个数(而不是字符个数)。

length

The number of UTF-16 code units in the receiver.

1
2
Declaration
@property(readonly) NSUInteger length;

Discussion

This number includes the individual characters of composed character sequences, so you cannot use this property to determine if a string will be visible when printed or how long it will appear.

结语

文本很复杂,程序员还是需要了解其中的运作机制以便能正确处理它。