字符编码

字符编码

定义一套规则对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间建立对应关系,它是信息处理的一项基本技术。通常人们用符号集合(一般情况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不同状态的组合来存储和处理信息的。元件不同状态的组合能代表数字系统的数字,因此字符编码就是将符号转换为计算机可以接受的数字系统的数。

英文有 26 个字母,加大写字母,加符号,也才一百多个,所以美国人制定了一套编码规则来表示他们的语言,就是 ASCII 编码规则,有 127 个符号。ASCII 计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。计算机普及后,对于各国人民在他们地区内扩展,也叫本地化编码,这些不同国家地区自顾自的制定编码规则,造成跨国交流非常混乱。比如中文世界里的编码就要 GBK,GB2312,GB18030,BIG5。’GB‘就是国标的缩写,还有韩文日文的编码更多。。为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的 Code Point,以满足跨语言、跨平台进行文本转换、处理的要求。

在网络传输需要兼顾效率,就有了 UTF(UCS Transformation Format)编码规则(可变长规则)整部编码史的发展,也记录着文本数据记录方式的变迁。那么现在就可以很轻松的面对乱码了,计算机对字符的处理分为两个阶段,当编码和解码的规则不一样是就会产生乱码。解码的时候如果没有显示指定编码规则,那计算机就会更加文本开端的字符去猜测编码规则。

在字符编码中,我们往往会涉及到以下的关键内容:

  • character——字符,'a', '€', '中' 等, 都是一个字符

  • character set——字符集,字符的集合

  • coded character set——字符编码集,为每一个字符指定一个唯一的数字用来表示这个字符,这些数字组成的集合就是字符编**码集合,Unicode 就是一个字符编码集

  • code point——代码点,是一个数字,用来表示字符集中的一个字符,也就是字符编码集中的一个数,例如 Unicode 编码中, 'A'的 code point 就是 65(在 Unicode 中通常写作 U+0041)

  • code space——代码空间,就是一个编码集中,code point 的范围,例如 Unicode 编码的 code space 就是 0x0000 - 0x10FFFF

  • character encoding scheme——字符编码方案,它定义了将字符用一个或多个固定长度的代码单元的方案,如前文提到的"utf-8 编码方式"就是一个字符编码方案,其它的还有 UTF16,UTF32,GBK 等等

  • code unit——编码单元,就是编码方案中固定长度的最小编码单元,如 UTF8 的编码单元是 1bit,UTF16 是 2bit,UTF32 是 4bit,

ASCII

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本 EASCII 则可以部分支持其他西欧语言,并等同于国际标准 ISO/IEC 646。ASCII 第一次以规范标准的类型发表是在 1967 年,最后一次更新则是在 1986 年,至今为止共定义了 128 个字符。

  • 32 个控制字符:十进制(0~31),比如 ESC (escape),二进制表示为 00011011。

  • 空格(space),二进制表示为 00100000;DEL (delete),二进制表示为 01111111。

  • 标点以及运算符:(33~47,58~64,91~96,123~126),比如‘+’表示为 00101011。

  • 数字(48~57),大写字母(65~90),小写字母(97~122)。比如‘a’表示为 01100001。

ASCII 是美国人设计的标准,只能支持 26 个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如 naïve、café、élite 等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。EASCII 扩展 ASCII(Extended ASCII)采用 8 位编码,基本解决了西欧语言的字符编码问题,但是对于欧洲其它地方如北欧,东欧地区,256 个字符还是不够用,如是出现了 ISO 8859。

ISO 8859

为解决 256 个字符不够用的问题,ISO 8859 采取的不再是单个独立的编码规则,而是由一系列的字符集(共 15 个)所组成,分别称为 ISO 8859-n(n=1,2,3…11,13…16,没有 12)。其每个字符集对应不同的语言,如 ISO 8859-1 对应西欧语言,ISO 8859-2 对应中欧语言等。其中大家所熟悉的 Latin-1 就是 ISO 8859-1 的别名,它表示整个西欧的字符集范围。需要注意的一点的是,ISO 8859-n 与 ASCII 是兼容的,即其 0000000(0x00)-01111111(0x7f)范围段与 ASCII 保持一致,而 10000000(0x80)-11111111(0xFF)范围段被扩展用到不同的字符集。

ISO 8859 是在 1980 年代中期甚至 1990 年代才陆续公布的。因此,微软公司与 IBM 公司等此前已经在其产品,如 MS-DOS,IBM PC 上使用了各自定义的编码字符集(即“代码页”Codepage)。ISO 8859 公布后,也出现了一些广泛使用的代码页兼容并扩充了 ISO 8859。例如,Windows 代码页 1252 作为英文及一些西欧语言版 Windows 操作系统的默认编码(locale),是 ISO 8859-1 的超集。主要扩充之处是把 ISO 8859-1 的保留未用的 C1 区(即码位 0x80-0x9F)用来编码一些可打印字符:€ ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ 共计 27 个,其中各种引号就有 8 个。

GB 系列

中国国家标准总局制定了 GB 2312 码,即中华人民共和国国家汉字信息交换用编码,并于 1981 年 5 月 1 日实施。GB 2312 字符集中除常用简体汉字字符外还包括希腊字母等可能会用到的字符,但是未收录繁体中文汉字和一些生僻字。

微软利用 GB 2312-80 未使用的编码空间,收录 GB 13000.1-93 全部字符制定了 GBK 编码。根据微软资料,GBK 是对 GB2312-80 的扩展,也就是 CP936 字码表 (Code Page 936)的扩展(之前 CP936 和 GB 2312-80 一模一样),最早实现于 Windows 95 简体中文版。虽然 GBK 收录 GB 13000.1-93 的全部字符,但编码方式并不相同。GBK 自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司公布为”技术规范指导性文件”。

GB 18030,全称:“国家标准 GB 18030-2005《信息技术 中文编码字符集》”,是中华人民共和国现时最新的变长度多字节字符集,是 GB 18030-2000《信息技术 信息交换用汉字编码字符集 基本集的扩充》的修订版。对 GB 2312-1980 完全向后兼容,与 GBK 基本向后兼容;支持 GB 13000 及 Unicode 的全部统一汉字,共收录汉字 70244 个。GB 18030 主要有以下特点:

  • 采用变长多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。

  • 编码空间庞大,最多可定义 161 万个字符。

  • 支持中国国内少数民族文字,不需要动用造字区。

  • 汉字收录范围包含繁体汉字以及日韩汉字。

  • GB 18030 对应 Windows 代码页为 CP54936。

Big5,又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录 13,060 个汉字。中文码分为内码及交换码两类,Big5 属中文内码,知名的中文交换码有 CCCII、CNS11643。Big5 虽普及于台湾、香港与澳门等繁体中文通行区,但长期以来并非当地的国家标准,而只是业界标准。倚天中文系统、Windows 等主要系统的字符集都是以 Big5 为基准,但厂商又各自增加不同的造字与造字区,派生成多种不同版本。2003 年,Big5 被收录到 CNS11643 中文标准交换码的附录当中,取得了较正式的地位。这个最新版本被称为 Big5-2003。Big5 对应 Windows 代码页为 CP950。

Unicode

Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。Unicode 是为了解决传统的字符编码方案的局限而产生的,例如 ISO 8859-1 所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不兼容的情况。Unicode 伴随着通用字符集(Universal Character Set)的标准而发展,至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为 2016 年 6 月 21 日公布的 9.0.0,已经收入超过十万个字符。Unicode 备受认可,并广泛地应用于电脑软件的国际化与本地化过程。有很多新科技,如 XML、Java 编程语言以及现代的操作系统,都采用 Unicode 编码。

Unicode 的实现方式不同于编码方式。一个字符的 Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对 Unicode 编码的实现方式有所不同。Unicode 的实现方式称为 Unicode 转换格式(Unicode Transformation Format,简称为 UTF)。Unicode 的实现方式有 UTF-8、UTF-16,另外还包括 UTF-7、Punycode、CESU-8、SCSU、UTF-32、GB18030 等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是 UTF-16 小端序(LE)、UTF-16 大端序(BE)和 UTF-8。在微软公司 Windows XP 附带的记事本(Notepad)中,“另存为”对话框可以选择的四种编码方式除去非 Unicode 编码的 ANSI(对于英文系统即 ASCII 编码,中文系统则为 GB2312 或 Big5 编码)外,其余三种为“Unicode”(对应 UTF-16 LE)、“Unicode big endian”(对应 UTF-16 BE)和“UTF-8”。

可以这样理解:Unicode 是字符集,UTF-32/ UTF-16/ UTF-8 是三种字符编码方案。

UTF-8

UTF-16

UTF-32

优点

直接兼容 ASCII;空间利用率高;无字节序问题;

通常情况每个字符占 2 个字节;

每个字符都使用 4 个字节,定位串中第 N 个字符为 O(1);

缺点

变长字节编码,寻找串中第 N 个字符复杂度为 O(N);

存在字节序问题;

空间利用率低;存在字节序问题;

UTF-8

UTF-8 的编码规则:

  • 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 unicode 码。

  • 对于 n 字节的符号(1<n<5),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为 10。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。

Unicode 符号范围

UTF-8 编码方式

U+0000 → U+007F

0xxxxxxx

U+0080 → U+07FF

110xxxxx 10xxxxxx

U+0800 → U+FFFF

1110xxxx 10xxxxxx 10xxxxxx

U+10000 → U+1FFFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

以汉字“学”为例,“学”的 Unicode 码值为 U+5b66,处于上面表格的 U+0800 → U+FFFF 段内,因此 UTF-8 编码用三个字节来存储。然后将 U+5b66 从最低位到最高位依次填入到上面格式中的 x,并在多出的高位上补 0 即可。5b66 转换为二进制:01011011 01100110,填入到 1110xxxx 10xxxxxx 10xxxxxx 中的 x 标记位,结果为:11100101 10101101 10100110,这样就得到了“学”的 UTF-8 编码,转换为 16 进制就是 e5ada6。

UTF-16

UTF-16 编码规则:

  • 从 U+D800 到 U+DFFF 的码位(代理区):因为 Unicode 字符集的编码值范围为 0-0x10FFFF,而大于等于 0x10000 的辅助平面区的编码值无法用 2 个字节来表示,所以 Unicode 标准规定:基本多语言平面内,U+D800..U+DFFF 的值不对应于任何字符,为代理区。

  • 从 U+0000 至 U+D7FF 以及从 U+E000 至 U+FFFF 的码位:第一个 Unicode 平面(BMP),码位从 U+0000 至 U+FFFF(除去代理区),包含了最常用的字符。UTF-16 与 UCS-2 编码在这个范围内的码位为单个 16 比特长的码元,数值等价于对应的码位。

  • 从 U+10000 到 U+10FFFF 的码位:辅助平面(Supplementary Planes)中的码位,大于等于 0x10000,在 UTF-16 中被编码为一对 16 比特长的码元(即 32bit,4Bytes),称作 code units called a 代理对(surrogate pair),具体方法是:

    • 码位减去 0x10000, 得到的值的范围为 20 比特长的 0..0xFFFFF(因为 Unicode 的最大码位是 0x10ffff,减去 0x10000 后,得到的最大值是 0xfffff,所以肯定可以用 20 个二进制位表示),写成二进制形式:yyyy yyyy yyxx xxxx xxxx。

    • 高位的 10 比特的值(值的范围为 0..0x3FF)被加上 0xD800 得到第一个码元或称作高位代理(high surrogate), 值的范围是 0xD800..0xDBFF。由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode 标准现在称高位代理为前导代理(lead surrogates)。

    • 低位的 10 比特的值(值的范围也是 0..0x3FF)被加上 0xDC00 得到第二个码元或称作低位代理(low surrogate), 现在值的范围是 0xDC00..0xDFFF。由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode 标准现在称低位代理为后尾代理(trail surrogates)。

    • 最终的 UTF-16(4 字节)的编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

BOM

字节顺序标记(英语:byte-order mark,BOM)是位于码点 U+FEFF 的统一码字符的名称。当以 UTF-16 或 UTF-32 来将 UCS/统一码字符所组成的字符串编码时,这个字符被用来标示其字节序。它常被用来当做标示文件是以 UTF-8、UTF-16 或 UTF-32 编码的记号。

字符 U+FEFF 出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。对于 GB 2312、GBK、GB 18030、Big5,其编码某个汉字产生的字节顺序,由其编码方案本身决定,不受 CPU 字节序的影响。UTF-8 无字节序问题。

编码

表示(十六进制)

表示(十进制)

UTF-8

EF BB BF

239 187 191

UTF-16(大端序)

FE FF

254 255

UTF-16(小端序)

FF FE

255 254

UTF-32(大端序)

00 00 FE FF

0 0 254 255

UTF-32(小端序)

FF FE 00 00

255 254 0 0

UTF-7

2B 2F 76 和以下的一个字节:[ 38 | 39 | 2B | 2F ]

43 47 118 和以下的一个字节:[ 56 | 57 | 43 | 47 ]

UTF-1

F7 64 4C

247 100 76

UTF-EBCDIC

DD 73 66 73

221 115 102 115

Unicode 标准压缩方案

0E FE FF

14 254 255

BOCU-1

FB EE 28 及可能跟随着FF

251 238 40 及可能跟随着255

GB-18030

84 31 95 33

132 49 149 51