
一文读懂字符编码(ASCII、ISO 8859、GB系列、Unicode)
\quad 前言:计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。如何将二进制数转换成英文、汉字等字符就是字符集和字符编码所作的事情。字符集和字符编码不是一个概念,字符集是由一系列系统支持的抽象字符所组成的集合;字符编码则规定了这些字符在计算机内部的二进制存储方式。在大多数情况下,这两者在信息科学中概念相同,例:‘
ASCII
’,‘
ISO8859
’;但有时也不同,例:‘
EUC-CN
’是字符集,‘
GB2312
’的一种编码。
注:
本文中所说的字符码(码点)与字符编码不是一种概念,
字符码指的是字符集中每个字符的数字编号。
\quad
常见字符集名称:ASCII
字符集、GB2312
字符集(区位码)、GB18030
字符集、Unicode
字符集等。每种字符集都有自己的字符编码规则,常用的字符集编码规则有ASCII
编码、UTF-8
编码、GBK
编码、Big5
编码等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
一、字符编码相关组织
1.1 ANSI 美国国家标准学会
\quad
美国国家标准学会(American National Standards Institute
,简称ANSI
)成立于1918
年。当时,美国的许多企业和专业技术团体,已开始了标准化工作,但因彼此间没有协调,存在不少矛盾和问题。为了进一步提高效率,数百个科技学会、协会组织和团体,均认为有必要成立一个专门的标准化机构,并制订统一的通用标准。
1918
年,美国材料试验协会(ASTM
)、与美国机械工程师协会(ASME
)、美国矿业与冶金工程师协会(ASMME
)、美国土木工程师协会(ASCE
)、美国电气工程师协会(AIEE
)等组织,共同成立了美国工程标准委员会(AESC
)。美国政府的三个部(商务部、陆军部、海军部)也参与了该委员会的筹备工作。1928
年,AESC
改组为美国标准协会(ASA
)。1966
年8
月,又改组为美利坚合众国标准学会(USASI
)。1969
年10
月6
日改成现名:美国国家标准学会(ANSI
)。
\quad
ANSI
是非赢利性质的民间标准化团体,但它实际上已成为国家标准化中心。美国国家标准局(NBS
)的工作人员和美国政府的其他许多机构的官方代表也通过各种途径来参与美国国家标准学会的工作。
\quad
ANSI
是国际标准化组织(ISO
)和国际电工委员会(IEC
)5
个常任理事成员之一,4
个理事局成员之一,参加79%
的ISO/TC
的活动,参加89%
的IEC/TC
活动(TC
是指标准化技术委员会)。ANSI
是泛美技术标准委员会(COPANT
)和太平洋地区标准会议(PASC
)的成员。
\quad
ANSI
发布了ASCII
编码。
1.2 Ecma 国际
\quad
Ecma
国际(Ecma International
)是一家国际性会员制度的信息和电信标准组织。1994
年之前,名为欧洲计算机制造商协会(European Computer Manufacturers Association
,简称ECMA
)。因为计算机的国际化,组织的标准牵涉到很多其他国家,因此组织决定改名表明其国际性。现名称已不属于首字母缩略字。
\quad
该组织在1961
年成立于日内瓦,旨在对欧洲的计算机系统进行标准化。与国家政府标准机构不同,Ecma
国际是企业会员制的组织,在欧洲制造、销售或开发计算机和电信系统的公司都可以申请成为会员。该组织的标准化过程比较商业化,自称这种营运方式减少官僚追求效果。
\quad
1982
年开始,ANSI
与ECMA
合作使用单字节ASCII
编码以外的区域,存储及表示使用拉丁字母的语言、使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等。1985
年,正式公布了ECMA-94
标准,即后来的ISO/IEC 8859 parts 1
, 2
, 3
, 4
。第5
、6
、7
、8
、9
、10
、11
、12
、13
、14
、15
、16
部分分别公布于1988
年、1987
年、1987
年、1987
年、1989
年、1992
年、2001
年、1997
年(正式宣布放弃研发)、1998
年、1998
年、1999
年、2001
年。
1.3 ISO/IEC
\quad
ISO
为国际标准化组织(International Organization for Standardization
),是目前世界上最大、最有权威性的国际标准化专门机构。国际标准化组织的前身是国家标准化协会国际联合会和联合国标准协调委员会。1946
年10
月,25
个国家标准化机构的代表在伦敦召开大会,决定成立新的国际标准化组织,定名为ISO
。1947
年2
月23
日,国际标准化组织正式成立。
\quad
IEC
为联合国下属的国际电工委员会(International Electro-technical Commission
),成立于1906
年,它是世界上成立最早的国际性电工标准化机构,负责有关电气工程和电子工程领域中的国际标准化工作。国际电工委员会的总部最初位于伦敦,1948
年搬到了位于日内瓦的现总部处。1887
~ 1900
年召开的6
次国际电工会议上,与会专家一致认为有必要建立一个永久性的国际电工标准化机构,以解决用电安全和电工产品标准化问题。1904
年在美国圣路易召开的国际电工会议上通过了关于建立永久性机构的决议。1906
年6
月,13
个国家的代表集会伦敦,起草了IEC
章程和议事规则,正式成立了国际电工委员会。1947
年作为一个电工部门并入国际标准化组织(ISO
),1976
年又从ISO
中分立出来。
\quad
ISO
联合IEC
制定标准,并且将一些现有的标准纳入国际标准,例:ASCII
对应ISO/IEC 646
;ECMA-35
对应ISO/IEC 2022
;ECMA-94
对应ISO/IEC 8859-1
~ ISO/IEC 8859-4
。
1.4 统一码联盟
\quad
统一码联盟(The Unicode Consortium
)是统筹Unicode
发展的非营利组织,其宗旨为最终以Unicode
取代现存的字符编码,因为现存编码不能够在多语言电脑环境中使用,而且字符数有局限。同时它也制定了数种Unicode
转换格式(Unicode Transformation Format
,UTF
)。Unicode
的成功让电脑使用进入了一个新纪元,并应用于很多新技术,如XML
、Java
编程语言和现今的操作系统。
\quad
统一码联盟有来自多个国家政府和各大软件商的代表参与。统一码联盟积极与各标准制订机构合作,包括国际标准化组织(ISO
)、国际电工委员会(IEC
)、万维网联盟(W3C
)、互联网工程工作小组(IETF
)和Ecma
国际等。
二、单字节字符系统
\quad
单字节字符系统(Single-Byte Character System
,SBCS
),我们可以从名称知道该系统的字符可以只用7
或8
比特表示,换句换说,系统包含最多128
或256
个字符。例:‘ASCII
’和‘ISO 8859
’,都是世界上最流行的单字节字符系统,还有专门用于日文的‘JIS X 0201’。
2.1 ASCII 字符集 & 字符编码
2.1.1ASCII 字符集
\quad
1960
年代初期,美国国会图书馆(Library of Congress
,简称LC
)的Henriette Avram
等人开始研拟机读编目格式。同时,James Agenboard
等人为此开始制订英文的字符集与交换码,作为图书馆界书目交换的共同标准。LC
交换码遂成为美国信息交换标准码ASCII
(American Standard Code for Information Interchange
)的雏形。1967
年,美国国家标准协会ANSI
(American National Standards Institute
) 制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,作为计算机数据传输的标准码,这就是ASCII
字符集(Character Set
),也称ASCII
(American Startand Code for Information Interchange
) 码,译作美国标准信息交换码。
\quad
ASCII
码一共规定了128
个字符的编号,由95
个可打印字符(编号32
~ 编号126
)和33
个控制字符(编号0
~ 编号31
,编号127
)组成。可打印字符用于显示在输出设备上,例:空格SPACE
编号是32
,大写的字母A
编号是65
;控制字符用于向计算机发出一些特殊指令,例:编号7
会让计算机发出哔的一声。ASCII
码对照表如下:
\quad
1972
年,国际标准化组织(ISO
)和国际电工委员会(International Electro-technical Commission
, IEC
)基于ASCII
制定了一项国际标准,被称为 ISO/IEC 646
标准(简称ISO 646
),它是一个7-bit
字符的字集。ISO/IEC 646
也被ECMA
批准为ECMA-6
。
2.1.2 ASCII 编码方式
\quad
我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit
)有0
和1
两种状态,因此八个二进制位就可以组合出256
种状态,这被称为一个字节(byte
)。也就是说,一个字节一共可以用来表示256
种不同的状态,每一个状态对应一个符号,就是256
个符号,从00000000
到11111111
。
\quad
上个世纪60
年代,美国制定了一套字符编码规则,对英语字符与二进制位之间的关系做了统一规定,这编码规则被称为ASCII
编码,一直沿用至今。ASCII
字符编码规定了将ASCII
字符集转换为计算机可接受的二进制数字系统的规则:使用一个字节的后7
位(bits
)表示一个字符,共128
字符,最前面的一位(最高位)统一规定为0
,其余位置将字符编号相应的转成二进制数字进行存储即可。例:空格SPACE
的ASCII
字符编码是00100000
,大写的字母A
的ASCII
字符编码是01000001
。
\quad
ASCII
编码的最大缺点是只能显示26
个基本拉丁字母、阿拉伯数字和英式标点符号,因此它只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve
、café
、élite
等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。
\quad
早期一般认为字符集和字符编码是同义词,并不需要进行严格区分。因为在传统字符编码模型中,基本上都是将字符集里的字符进行编号(字符编号转化为二进制数后一般不超过一个字节),然后该字符编号就是字符的编码。因此在像ASCII
这样的简单字符集为代表的传统字符编码模型中,这两个概念的含义几乎是等同的。
2.2 ISO 8859 字符集 & 字符编码
\quad
随着很多欧洲国家也开始使用计算机,人们发现使用128
个编号可以将英文符号一一对应,但是用来表示其他语言,128
个编号是不够的,例:在法语中,字母上方有注音符号,它就无法用ASCII
字符集表示。人们就在想,单字节能够表示的数字(编号)有256
个,而ASCII
字符只使用了前128
个,后面128
个数字还没有被使用。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号,例:法语中的é
的编码为130
。这样一来,这些欧洲国家使用的编码体系,可以表示最多256
个符号。这个表示方式被称为EASCII
(Extended ASCII
)。
\quad
但是,这里又出现了新的问题:不同的国家有不同的字母。因此,哪怕欧洲各国都使用256
个符号的编码方式,代表的字母却不一样,比如:130
在法语字符集中代表了é
;在希伯来语字符集中却代表了字母Gimel (ג)
;在俄语字符集中又会代表另一个符号。但是不管怎样,在所有这些字符集中,编号0 ~ 127
表示的符号是一样的,不一样的只是编号128 ~ 255
的这一段。
\quad
因为在EASCII
中表示的256
个字符中,前128
字符和ASCII
字符集表示的字符完全一样,后128
个字符欧洲各国或地区都有自己的字符集,所以为了统一各国各语言单独编码的混乱局面,欧洲计算机制造商协会(Ecma
国际)在上世纪80
年代中期开始陆续公布了ECMA-94
标准,之后被国际标准化组织收纳并命名为ISO 8859
。ISO8859
不是单个标准,而是一系列的子标准,这些子标准适用于欧洲不同的国家地区,ISO8859
字符集与编码系统的共同特色是:以同样的码位对应不同字符集。各种ISO 8859
字符集如下:
ISO8859-1
字符集,正式编号为ISO/IEC 8859-1:1998
,又称Latin-1
或“西欧语言”,收集了西欧常用字符,包括德法两国的字母。(曾推出过ISO 8859-1:1987
版)ISO8859-2
字符集,正式编号为ISO/IEC 8859-2:1999
,又称为Latin-2
或“中欧语言”,收集了中欧字符。(曾推出过ISO 8859-2:1987
版)ISO8859-3
字符集,正式编号为ISO/IEC 8859-3:1999
,又称为Latin-3
或“南欧语言”,收集了南欧字符。世界语也可用此字符集显示。(曾推出过ISO 8859-3:1988
版)ISO8859-4
字符集,正式编号为ISO/IEC 8859-4:1998
,又称为Latin-4
或“北欧语言”,收集了北欧字符。(曾推出过ISO 8859-4:1988
版)ISO8859-5
字符集,正式编号为ISO/IEC 8859-5:1999
或Cyrillic
,收集了斯拉夫语系字符。(曾推出过ISO 8859-5:1988
版)ISO8859-6
字符集,正式编号为ISO/IEC 8859-6:1999
或Arabic
,收集了阿拉伯语系字符。(曾推出过ISO 8859-6:1987
版)ISO8859-7
字符集,正式编号为ISO/IEC 8859-7:2003
或Greek
,收集了希腊字符。(曾推出过ISO 8859-7:1987
版)ISO8859-8
字符集,正式编号为ISO/IEC 8859-8:1999
或Hebrew
,收集了西伯莱(犹太人)字符。(曾推出过ISO 8859-8:1988
版)ISO8859-9
字符集,正式编号为ISO/IEC 8859-9:1999
,又称为Latin-5
或Turkish
,它把Latin-1
的冰岛语系字符换走,加入土耳其语系字符。(曾推出过ISO/IEC 8859-9:1989
版)ISO8859-10
字符集,正式编号为ISO/IEC 8859-10:1998
,又称为Latin-6
或Nordic
,收集了北欧(主要指斯堪地那维亚半岛)的字符,用来代替Latin-4
。(曾推出过ISO/IEC 8859-10:1992
版)ISO8859-11
字符集,正式编号为ISO/IEC 8859-11:2001
或Thai
,它是从泰国的TIS620
标准字符集演化而来。ISO8859-12
字符集,目前尚未定义。ISO8859-13
字符集,正式编号为ISO/IEC 8859-13:1998
,又称为Latin-7
或Baltic Rim
,主要涵盖波罗的海(Baltic
)诸国的文字符号,也补充一些在Latin-6
中遗漏的拉脱维亚(Latvian
)字符。ISO8859-14
字符集,正式编号为ISO/IEC 8859-14:1998
,又称为Latin-8
或Celtic
,它将Latin-1
中的某些符号换成凯尔特语(Celtic
)的字符。凯尔特族是指英伦外围的威尔斯人(Welsh
)和盖尔人(Gaelic
)。ISO8859-15
字符集,正式编号为ISO/IEC 8859-15:1999
,又称为Latin-9
,俗称Latin-0
,这个字符集于1998
年制定,它将Latin-1
中较少用到的符号删除,加入Latin-1
欠缺的芬兰语字母和大写法语重音字母,以及欧元(€
)符号。ISO 8859-16
字符集,正式编号为ISO/IEC 8859-16:2001
,又称Latin-10
或“东南欧语言”,是国际标准化组织于2000
年制定的ISO/IEC 8859
8 8 8 位字符集。这个字符集主要涵盖阿尔巴尼亚语、克罗地亚语、匈牙利语、意大利语、波兰语、罗马尼亚语及斯洛文尼亚语等东南欧国家语言,并加入欧元(€
)符号。
三、多字节字符系统
\quad
多字节字符系统(Multi-Byte Character System
,MBCS
),相对于主要用于拉丁语系国家的SBCS
来说,MBCS
主要是为拥有更多字符需要表示的其他国家服务,例:中国、日本等。显然,1
个字节不足以表示那么多的字符。
3.1 ISO/IEC 2022 通用技术规范
\quad
ISO 2022
,全称ISO/IEC 2022
,由国际标准化组织(ISO
)及国际电工委员会(IEC
)联合制定,是一个使用7
位或8
位编码表示各种语言文字的通用技术规范。ISO 2022
既不是字符集,也不是字符编码,而是一种标准或者说代码扩展规定,包括:多重字符集的编码,以及切换字符集与字符编码的方式。在ISO/IEC 2022
的延伸编码结构中,可按照字符集的大小选定其字符编码为单字节或多字节,而每一个字节则可选择为7bit
或8bit
。中国国标GB 2312
、日本工业规格JIS X 0202
(旧称JIS C 6228
)及韩国工业规格KS X 1004
(旧称KS C 5620
)均遵从ISO 2022
。
3.1.1 7-bit 环境
\quad
1963
年公布的ASCII
码是第一个得到广泛采用的7
位字符编码。这时的通信领域的协议采用了第8
位做校验纠错用途。但是,对于计算机内存来说,校验纠错变得不是必要。因此8
位字符编码逐渐出现,用来表示比ASCII
码更多的字符。为此,1971
年公布的ECMA-35
标准,用来规定各种7
位或8
位字符编码应当遵从的共同规则。随后ECMA-35
被采纳为ISO 2022
。
\quad
ISO 2022
用于兼容当时的7
比特宽的通信协议/通信设备。
- 对于单字节的
7-bit
编码空间,ISO/IEC 2022
规定可以做两种不同方式的划分:34
个控制字符(\0x00
(0
) ~\0x20
(32
) 加上\0x7F
(127
))和94
个图形字符(\0x21
(33
) ~\0x7E
(126
));或者32
个控制字符(\0x00
(0
) ~0x1F
(31
))和96
个图形字符(\0x20
(32
) ~\0x7F
(127
))。 - 针对多字节的
7-bit
编码空间,ISO/IEC 2022
规定:该字符集的图形字符编码,必须完全由两个或多个属于94
个图形字符(\0x21
(33
) ~\0x7E
(126
)),或是完全由两或多个属于96
个图形字符(\0x20
(32
) ~\0x7F
(127
))所组成,两者不得彼此混和使用,更不可以夹杂任何控制字符。- 对于双字节的
7-bit
编码空间,图形字符可以有94 x 94 = 8,836
(或96 × 96 = 9,216
)个。 - 对于三字节的
7-bit
编码空间,图形字符可以有94 × 94 × 94 = 830584
(或96 × 96 × 96 = 884,736
)个(虽然没有三字节字符集向ISO
登记)。
- 对于双字节的
\quad
英语可用7
位编码储存,而其他使用拉丁字母、希腊字母、西里尔字母、希伯来字母等的语言,由于只使用数十个字母,传统上均使用8
位编码的ISO/IEC 8859
标准来表示。但由于汉语、日语及朝鲜语字数众多,无法用单一个8
位字符来表达,故需要多于一个字节来代表一个字。于是,ISO 2022
就设计出来让汉语、日语及朝鲜语可以使用数个7
位编码的字符来表示。
3.1.2 8-bit 环境
\quad
对于遵从ISO 2022
的单字节8-bit
编码字符集,ISO/IEC 2022
先将每个字节的编码空间划分为左、右两个编码子空间,左编码子空间为码位\0x00
(0
) ~\0x7F
(127
)(二进制最高位为0
),右编码子空间为码位\0x80
(128
) ~ \0xFF
(255
)(二进制最高位为1
)。每个编码子空间再分别按照7-bit
方式,划分图形字符和控制字符。这种8-bit
编码字符集很容易兼容当时的7-bit
宽的通信协议/通信设备。
\quad
针对多字节8-bit
编码字符集,ISO/IEC 2022
规定:该字符集的多字节8-bit
图形字符,必须遵循上一节7-bit
的规定(即图形字符编码必须完全由两个或多个94
图形字符,或是由两个或多个96
图形字符所组成),并且每字节的最高位必须都为1
或0
。
\quad
综上所述,单字节8-bit
编码空间可以提供94+94 = 188
(或96+96 = 192
)个图形字符;双字节8-bit
编码空间可以提供94×94+94×94 = 17,672
(或96×96 + 96×96 = 18,432
)个图形字符,而非188×188
(或192×192
)个图形字符,因为要求每字节的最高位都为1
或0
,只有两个选择,可以看成(188×188)/2
(或(192×192)/2
);同理,三字节8-bit
编码空间只可以提供94×94×94 + 94×94×94 = 1,661,168
(或96×96×96 + 96×96×96 = 1,769,472
)个图形字符,而不是188×188×188
(或192×192×192
)个图形字符;依此类推。换言之,就ISO/IEC 2022
所规定的延伸编码而言,不论字符编码的长度是多少字节,8-bit
所能提供的码位,其实只有2
倍于7-bit
所能提供的,而非
2
n
2^n
2n倍(
n
n
n为字节数)。
3.1.3 编码结构
\quad
ISO 2022
规定字符集的控制字符可分为两块:C0
,C1
;图形字符可分为四块:G0
,G1
,G2
,G3
。对于单字节7-bit
编码, 字节值\0x00
(0
) ~ \0x1F
(31
) 保留给C0
控制字符块,字节值\0x20
(32
) ~ \0x7F
(127
) 用于G0
,G1
,G2
,G3
字符块;对于双字节7-bit
编码的字符集,1
个图形字符块可包含94 x 94
个字符,可以使用控制符的转义序列来表示在G0
,G1
,G2
,G3
之间的切换。
\quad
8-bit
字符编码时,\0x00
~ \0x1F
表示C0
或称CL
区(L
是left
缩写,因为其在字符表的左侧),\0x80
~ \0x9F
表示C1
或称CR
(R
是Right
缩写,因为其在字符表的右侧)。\0x20
~ \0x7F
表示G0
(称GL
区),\0xA0
~ \0xFF
(称GR
区)可表示G1
, G2
, G3
。(默认GL
区指G0
字符,GR
区指G1
字符)。如下表所示:
\quad
ISO/IEC 2022
的图形字符编码结构,由三部分构成,如图所示:

- 现用字符集区:收容让电脑据此处理图形字符编码的字符集,除非字符编码之前附加调用控制符,否则电脑会将所读取的任何图形字符编码都当做现用字符集里的某个字符而加以处理或显示。在
7-bit
环境下,现用字符集区只有单一的编码空间。在8-bit
环境下,现用字符集区则区分成左、右两个编码子空间。 - 备用字符集区:收容最多四个备用的图形字符集,分别称为
G0
、G1
、G2
和G3
。 - 图形字符集库:收容电脑处理文字、数字信息所需的全部图形字符集,其数量可多达上千个。这些字符集的字符编码,可以是单字节或多字节,每个字节可以是
7-bit
或8-bit
,详情如前文所述。
\quad
ISO/IEC 2022
规定电脑必须先利用逃逸序列(escape sequence
)控制符从图形字符集库中将最多四个图形字符集分别载入备用字符集区。然后再借由调用(invocation
)控制符,从四个备用字符集G0
、G1
、G2
和G3
当中挑选其一,载入现用字符集区,让电脑可据以处理信息或显示字符图形。每个图形字符集及字符编码都必须赋予特定的逃逸序列控制符,并需先向ISO
登记注册,以便不同电脑之间能有共同遵循的依据,而不至于将字符编码对应到错误的字符集。但是,如果许多电脑共同使用不超过四个的图形字符集,则可经由彼此约定,将这些字符集分别预设为G0
、G1
、G2
和G3
,因而可省略逃逸序列控制符。
\quad
ISO/IEC 2022
供单字节图形字符编码选用的三字节逃逸序列控制符,以及供多字节图形字符编码选用的四字节逃逸序列控制符如下表所示。这些逃逸序列控制符里的终结字符[F]
,其字符编码依规定必须是从\0x30
至\0x7E
的7-bit
编码,共计79
个字符。而每个终结字符[F]
可用来指定一个图形字符集和字符编码,因此共计可用以指定553
个单字节和553
个多字节的图形字符集和字符编码。除此之外,ISO/IEC 2022
还规定了供多字节图形字符编码选用的三字节和超过四字节的逃逸序列控制符。
\quad
ISO/IEC 2022
所规定的调用控制符名称及字符编码串如下表所示。针对7-bit
环境,ISO/IEC 2022
定义了4
个锁定移位(locking-shift
)和2
个单一移位(single-shift
)的调用控制符;针对8-bit
环境,ISO/IEC 2022
则定义了7
个锁定移位和2
个单一移位的的调用控制符。
\quad
一旦使用对应的锁定移位调用控制符将四个备用字符集(G0
、G1
、G2
和G3
)之一载入到现用字符集区之后,就会成为常驻字符集。若要将现用常驻字符集切换为另一个备用字符集,必须再借助对应的锁定移位调用控制符,重新将目标字符集载入现用常驻字符集区。至于单一移位(single-shift
)调用控制符则只对紧跟其后的一个字符编码发生作用。换言之,当电脑遇有单一移位调用控制符时,就会将紧跟于控制符之后的一个字符编码,当做该调用控制符所指定的备用字符集里的某个字符而加以处理或显示。但是,下一个字符编码则恢复为适用现用常驻字符集。
\quad
ISO 8859-X
字符集是特定的把ISO 2022
的若干成分组合起来的字符集。这些成分包括:低端控制字符(C0
)、空格字符及删除字符(34
个);ASCII
字符集(GL
,94
个);高端控制字符(C1
,32
个);高端字符(GR
,96
个)是特定于每个ISO 8859-X
变种的字符,例:ISO-8859-1
是由ISO-IR-1
、ISO-IR-6
、ISO-IR-77
、ISO-IR-100
组成。
\quad
对于GB 2312
,是双字节8-bit
编码。其汉字编码空间为94 x 94
,即有94
个区,每个区有94
个位(用来编码字符)。实际使用了16
~ 55
区编码一级汉字,56
~ 87
区编码二级汉字。这些汉字均放在了GR
字符块区,可表示G1
, G2
, G3
。字节值在\0x00
~ \0x7F
为单字节表示一个字符,构成了C0
、G0
区,与ASCII
码兼容。因此,GB 2312
是单、双字节混合编码。
- 注:
GBK
编码作为Windows
操作系统(简体中文版)的缺省语言locale
设置,GBK
编码虽然完全向后兼容GB 2312
,但GBK
突破了ISO 2022
中GR
区域的字数限制:94 x 94 = 8,836
个字。
\quad
ISO 2022
使用“转义序列”(Escape sequence
)指出随后的字符属于哪个字符集。这些字符集在ISO
登记,并遵循ISO 2022
标准规定的模式。
代码 | Hex | 缩写 | 名称 | 效果 |
---|---|---|---|---|
ESC ! A | 1B 21 | CZD | C0-designate | 使用C0 控制符 |
ESC “ A | 1B 22 | C1D | C1-designate | 使用C1 控制符 |
ESC % | 1B 25 | DOCS | Designate other coding system | 8比特编码;使用ESC % @重置回ISO 2022 |
ESC % / | 1B 25 2F | DOCS | Designate other coding system | 8比特编码;无法重置 |
3.2 ISO/IEC 10646
\quad
随着计算机功能的日趋强大与价格的日趋便宜,其应用领域也越来越广。但是随之而来的各种编码需求,却使得单一字节的编码方式,因编码空间太小,变得不足以因应各种应用程序的需求。中文字、排版系统的标志符号、非英语拼音字母和图形符号等的编码,需要使用2
个或多个字节来编码。同时,为了预防这些多字节字符码被计算机或网络设备“吃掉”其中的某个字节,编码时必须避开每个字节的0
~32
和127
这34
个字节值,这种做法严重浪费编码空间。
\quad
1984
年4
月,国际标准化组织(ISO
)的一些会员国发起制定新的国际计算机字符的编码标准,并成立了工作小组(ISO/IEC JTC1/SC2/WG2
),针对各国文字符号进行统一性编码的研发工作,最后定案的标准名为Universal Multiple-Octet Coded Character Set
(简称UCS
),编号则订为ISO/IEC 10646
,进一步成为全球计算机字符的编码标准。
3.3 GB 系列字符集 & 字符编码
- 前言:一段历史
1978
年,日本基于ISO 2022
制订了全世界最早的汉字编码JIS C 6226
。1980
年代,中国大陆、中国台湾、韩国各自制订了自己的规范。这些规范彼此之间并无关系。若要在一份文件中同时使用,则要以转义字符的方式来交换。1980
年,日本的国立国会图书馆的高桥德太郎以图书学的观点指出,一个统一的东亚汉字编码系统是有必要的。同年,中国台湾颁布了第一版三字节存储的中文资讯交换码(Chinese Character Code for Information Interchange
,CCCII
),这是第一个期望可以一致处理中国、日本、韩国汉字的编码。之后,美国的国会图书馆采用了此标准,并另外命名为东亚编码字符(East Asian Character Code
,EACC
,ANSI/NISO Z39.64
)。1984
年4
月,国际标准化组织(ISO
)的一些会员国发起制定新的国际计算机字符的编码标准,并成立了工作小组(ISO/IEC JTC1/SC2/WG2
),针对各国文字符号进行统一性编码的研发工作,最后定案的标准名为Universal Multiple-Octet Coded Character Set
(简称UCS
),编号则订为ISO/IEC 10646
,进一步成为全球计算机字符的编码标准。这个编码一开始的构想是采用16
位,而对于日本及中国等国的汉字编码则原封不动地加入。但若如此,中国当时所制订的编码都无法加入,因而反对,并在1989
年提出各国汉字统合集合(Han Character Collection
,简称HCC
)的构想。
3.1 GB2312
3.1.1 GB2312 字符集
\quad
随着计算机逐渐在中国普及,我们面临着与欧美各国之前遇到的相同问题,中国也迫切需要一种规则将汉字翻译为计算机可以存储的二进制数字。因为中文的每一个字都是一种新的写法,所以需要将每个汉字映射到一个二进制编号。而常用汉字就有6000
多个,之前欧美定义的使用单字节表示一个字符已经不能够表示汉字,于是人们想到了使用双字节来表示汉字,并且将这种表示方式命名为GB2312
(GB
,Guóbiāo
国标,中华人民共和国国家标准,简称国标,即国标2312
)。GB2312
字符集遵循ISO/IEC 2022
标准,将所有字符放在94x94
的格子中,也被称为区位码。将字符所在的区块称为区,区中的位置称为位。
\quad
GB2312
或GB2312-80
是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集-基本集》,又称GB0
,由中国国家标准总局发布,1981
年5
月1
日实施。GB2312
字符集通行于中国大陆;新加坡等地也采用此字符集。中国大陆几乎所有的中文系统和国际化的软件都支持GB2312
。GB2312
的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%
的使用频率。
\quad
GB2312
共收录有7445
个字符,其中简体汉字6763
个:包括一级(常用)汉字3755
个,二级(次常用)汉字3008
个。同时,GB2312
编码收录了包括拉丁字符、希腊字符、日文平假名及片假名字符、俄语西里尔字符在内的682
个全角字符。注:GB2312
字符集针对ASCII
字符集中原有的数字、标点、字母,重新赋予了两个字节长的编号,这就是常说的"全角"字符,而ASCII
字符集中原有的字符被称为"半角"字符。
\quad
GB2312
将所收录的字符分为94
个区,编号为01
区至94
区;每个区收录94
个字符,编号为01
位至94
位。GB2312
的每一个字符都由与其唯一对应的区号和位号(即码位)所确定,因此字符所在位置也被称为区位码。GB2312
字符集各区详解:
分区号 | 字符数 | 字符类别 |
---|---|---|
01 | 94 | 一般符号 |
02 | 72 | 顺序号码 |
03 | 94 | 拉丁字母 |
04 | 83 | 日文平假名 |
05 | 86 | 日文片假名 |
06 | 48 | 希腊字母 |
07 | 66 | 俄语西里尔字母 |
08 | 63 | 汉语拼音和注音符号 |
09 | 76 | 图形符号 |
10 ~ 15 | 无字符 | 备用区 |
16-55 | 3755个 | 一级(常用)汉字,按拼音排序 |
56-87 | 3008个 | 二级(次常用)汉字,按部首/笔画排序 |
88 ~ 94 | 无字符 | 备用区 |
\quad
区位码取值范围:0101
~ 9494
。例:“啊”字在16
区01
位,所以“啊”字的区位码是:16 01
,其中16
相当于高位字节,01
相当于低位字节。下图是GB2312
区位码的开始部分,点此可以查看更多区位码。
3.1.2 GB2312 编码方式
(1)国标码(交换码)
\quad
在日常计算机的使用过程中,我们不可避免的要使用到英文字符,而如果直接使用GB2312
的区位码进行计算机的编码,会导致有些字符无法正常显示。因为GB2312
区位码会与ASCII
码中0
~ 31
编码的不可显示字符和32
编码的空格字符发生识别错误,所以要使得GB2312
字符集兼容ASCII
字符集,就需要避开ASCII
字符中的不可显示字符(十六进制为\0x00
~ \0x1F
;十进制为0
~ 31
)及空格字符(十六进制为\0x20
,十进制为32
)。因此,须将 “区码” 和 “位码” 分别加上32
(十六进制为\0x20
),作为国标码,以避免与ASCII
码中0
~32
的不可显示字符和空格字符相冲突。注:因为 “区码” 和 “位码” 最小都是01
开始,所以加上32
即可。因此,国标码(交换码)取值范围就变成:\0x2121
~ \0x7E7E
。
\quad
例如: “万”字的区位码为:4582
,国标码十进制为:(45
+32
,82
+32
)=(77
,114
),十六进制为:(4D
,72
)。
- 有人可能会问:
ASCII
码中还有一个不可显示字符,编号127
的删除控制符,为什么没有考虑它?这是因为 “区码” 和 “位码” 最大数值都是94
,加上32
后是126
,正好没有到127
,所以无须考虑它。
(2)机内码(内码)
\quad
人们使用国标码解决了字符的显示问题,可是在计算机的使用过程中,出现了一个新的问题:使用GB2312
国标码将简体中文字符编码到二进制数字进行传输后,对方解码出的文字并不是我们要传输的内容,这就发生了乱码问题。
\quad
发生此问题的原因是GB2312
使用双字节存储简体中文字符,而我们使用区位码进行编码的这一过程中不会发生问题,在我们解码过程中,“区码” 和 “位码” 的取值范围与ASCII
码单字节的英文字母、数字和符号等可打印字符取值范围恰好相同,因此就会产生乱码。例: “万”字的国标码为:\0x4D72
,区码\0x4D
与M
冲突,位码\0x72
与r
冲突。由此可见,国标码并不完全兼容ASCII
码。
\quad
因此,为了解决这个问题,人们规定将国标码中每个字节的最高位都从0
换成1
,即相当于每个字节都再加上128
(十六进制为80
,即\0x80
;二进制为1000 0000
),从而得到国标码的 “机内码” 表示,简称 “内码”。此时,GB2312
内码才真正完全兼容ASCII
码,内码才是字符用GB2312
编码后在计算机中存储的形式,这种存储方式也被称为EUC-CN
编码(双字节8bit
编码)。GB2312
内码的取值范围:高字节从\0xA1
到\0xF7
,低字节从\0xA1
到\0xFE
。点此查看更多GB2312
机内码。
(3)EUC-CN
\quad
EUC
全名为Extended Unix Code
,是一个使用8
位编码来表示字符的方法。
3.2 GB13000.1-1993 标准
\quad
为了便于多个文种的同时处理,国际标准化组织(ISO
)下属编码字符集工作组研制了新的编码字符集:ISO/IEC 10646
《通用多八位编码字符集(Universal Multiple-Octet Coded Character Set
,简称UCS
)》,即Unicode 1.1
(后文详解了两者关系)。该字符集一共收录了20,902
个汉字。该标准第一次颁布是在1993
年,当时只颁布了其第一部分,即ISO/IEC 10646-1: 1993
,我国相应的国家标准是GB13000.1-1993
《信息技术通用多八位编码字符集(UCS
)第一部分:体系结构与基本多文种平面》。制定这个标准的目的是对世界上的所有文字统一编码,以实现世界上所有文字在计算机上的统一处理。
\quad
GB13000.1-1993
制定的原则与GB2312
不同,与GB2312
完全不兼容。然而,因为早期的计算机中的简体中文编码都已采用GB2312
,无法顺利向GB13000.1-1993
过渡,所以导致了GB13000.1-1993
无法大规模推广,最终被废止。
3.3 ANSI 编码
\quad
ANSI
指的是American National Standards Institute
(美国国家标准学会)。ANSI
编码不是一种具体的编码方式,而是一种指定在某些环境下使用某些编码方式的标准。例:在中文环境中ANSI
的编码标准为GBK
,在日语环境中ANSI
的编码标准则是Shift_JIS
编码。
\quad
不同的国家和地区制定了不同的标准,由此产生了GB2312
、GBK
、GB18030
、Big5
、Shift_JIS
等各自的编码标准。这些使用多个字节来代表一个字符的各种汉字延伸编码方式,这些编码统称为ANSI
编码。在Windows
系统的编码处理中,ANSI
编码一般代表系统默认的编码方式,而且并不是确定的某一种编码方式,一般与用户设置的Locale
相关(微软为了适应世界上不同地区用户的文化背景和生活习惯,在Windows
中设计了区域(Locale
)设置的功能)。例:在简体中文Windows
操作系统中,ANSI
编码代表GB2312
编码;在繁体中文Windows
操作系统中,ANSI
编码代表Big5
;在日文Windows
操作系统中,ANSI
编码代表JIS
编码。
\quad
不同ANSI
编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段ANSI
编码的文本中,例:无法用同一种ANSI
编码来存储既有汉字、又有日文的文本。对于ANSI
编码而言,\0x00
~ \0x7F
之间的字符,依旧是使用1
个字节表示(ASCII
编码),而这之外的字符通常是使用\0x80
~ \0xFFFF
范围内的两个字节来表示。
3.4 Code Page
\quad
Code Page
(代码页)是字符编码的别名,也称内码表,是特定语言的字符集的一张表,简称为CP
。IBM
和微软内部使用数字来标记不同的编码字符集,不同的厂商对同一个字符集编码使用各自不同的名称。例:UTF-8
在IBM
称作CP1208
,在微软称作CP65001
,在SAP
称作CP4110
。
3.4.1 OEM 代码页
\quad
早期,Code Page
是IBM
公司称呼计算机的BIOS
所支持的字符集编码。当时通用的操作系统都是命令行界面,这些操作系统直接使用BIOS
提供的字符绘制功能来显示字符(或者是一组嵌入在显卡字符生成器中的字形),这些BIOS
代码页也被称为OEM
代码页(或硬件代码页)。
\quad
1987
年4
月,IBM
发布了PC-DOS 3.3
,微软发布了几乎完全相同的MS-DOS 3.3
,正式向普通PC
用户引入了Code Page
(代码页)这个概念,并且允许用户在操作系统中使用Code Page
指令设置字符编码。这时的PC
机使用CGA
(Color Graphics Adaptor
,彩色图形适配器,IBM
公司的第一个彩色图形卡)显示系统的字符界面,绘制不同语言的字符需要依靠BIOS
硬件厂商(在当时就是指制定业界标准的IBM
)提供的功能。如果想更换操作系统所支持的字符集,就必须换上支持该字符集的硬件设备。微软作为DOS
操作系统的软件厂商,并不拥有绘制这些字符集的知识产权。最常见、最具代表性的OEM
代码页是 “IBM PC
或MS-DOS
代码页CP437
”。
3.4.2 Windows 代码页
\quad
随着用户图形界面操作系统的广泛使用(最初被广为接受的是Windows 3.1
),操作系统具有了字符绘制的功能。微软在Windows
操作系统没有转向UTF-16
作为内码实现之前(也就是在Windows NT 3.1
之前),针对不同的使用地区与国家,定义了一系列的支持不同语言字符集的代码页,被称作"Windows
(或ANSI
)代码页"。
\quad
在Windows
操作系统决定采用Unicode
编码开始(从Windows NT 3.1
开始,微软决定为其所有操作系统采用16-bit
(双字节)UTF-16
编码系统),ANSI
代码页逐渐被取代。补充知识:Microsoft Windows 1.01
是微软第一次对个人电脑操作系统进行用户图形界面的尝试,也是Windows
系列的第一个产品,于1985
年11
月20
日开始发行。Windows NT
(Windows New Technology
,新技术视窗操作系统)是美国微软公司1993
年推出的纯32
位操作系统核心。
\quad
ANSI Code Page
的官网正式叫法其实是Windows Code Page
。但是由于ANSI Code Page
被误用的太广泛了,索性微软也就接受了此叫法,然后就叫做ANSI Code Page
了。最具代表性的是实现了ISO 8859-1
(即Latin-1
)的代码页1252
(即CP1252
),以及实现了GBK
的代码页936
(即CP936
)。
3.5 GBK
3.5.1 GBK 字符集
\quad
随着信息技术在各行业应用的深入,GB2312-80
收录汉字数量不足的缺点已经初步显露出来。例:“镕” 字现在是高频率使用字,而 GB2312
却没有为它编码,因而,政府、新闻、出版、印刷等行业和部门在使用中感到十分不便。因此,微软公司对GB2312-80
进行了扩展,于1993
年定义了GBK
字符集,同时利用GB2312-80
中未使用的码点支持GB13000.1-93
中的字符(编码方式不同),GBK
对GB2312-80
具有向后兼容(向下兼容)性。
\quad
Microsoft
在Windows95
(1995
年8
月24
日正式发布)和Windows NT 3.51
(1995
年5
月发布)中实现了对GBK
编码的支持,虽然GBK
并不是官方标准,但Windows 95
的广泛使用使得GBK
成为了“事实标准”。于是,由中华人民共和国全国信息技术标准化技术委员会1995
年12
月1
日制订,国家技术监督局标准化司和电子工业部科技与质量监督司1995
年12
月15
日联合以《技术标函[1995]229
号》文件的形式公布了《汉字内码扩展规范》版本1.0
(Chinese Internal Code Specification
,即GBK 1.0
)。 GBK
共收录21886
个汉字和图形符号,其中汉字(包括部首和构件)21003
个,图形符号883
个。注:GBK
并非国家正式标准,只是国家技术监督局标准化司、电子工业部科技与质量监督司发布的 “技术规范指导性文件”。
\quad
GBK
字符集向下完全兼容GB2312-80
字符集且收录了GB13000.1-1993
中的全部字符,但编码方式完全不同。GBK
支持GB2312-80
中不支持的部分中文姓、中文繁体、日文假名,还包括希腊字母和俄语字母等字符,不过并不支持韩语字符,这也是它在实际使用中与unicode
编码相比欠缺的部分。向上兼容(向前兼容) ISO 10646-1
(Unicode
)国际标准,是GB2312
向ISO 10646-1
(Unicode
)过渡过程中的一个承上启下的产物。
3.5.2 GBK 编码方式
\quad
GBK
亦采用双字节表示,总体编码范围:\0x8140
~ \0xFEFE
(剔除\0x**7F
),首字节在81
~ FE
之间,尾字节在40
~ FE
之间(不含\0x7F
),总计23940
个码位,共收入21886
个汉字和图形符号,其中汉字(包括部首和构件)21003
个,图形符号883
个。全部编码分为三大部分:
- 汉字区,包括:
GB2312
汉字区。即GBK/2
:\0xB0A1
~\0xF7FE
。收录GB2312
汉字6763
个,按原顺序排列。GB13000.1-1993
扩充汉字区。包括:- GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。

4.4 GB 18030
国家质量技术监督局于2000
年3月17日推出了GB 18030-2000标准,以取代GBK。
2000 年,GB 18030-2000 正式发布,在取代 GBK 的同时,也保留了对 GBK 1.0 的兼容性。有时,GBK 也指 GB 18030 中使用 1 字节或 2 字节编码的字符。
最新的是 2005 年发布的 GB10830-2005 标准。自 2006 年 5 月 1 日起,在中华人民共和国境内销售的软件产品,都需要符合 GB10830 规范。
三、Unicode
\quad
为了统一世界各种语言的编码,unicode
编码被创造出来,需要注意的是,unicode
也不是一个具体的编码规则,在unicode
标准下,有utf-8
、utf-16
和utf-32
等具体的实现,他们都对应不同的编码规则。Unicode
被译为统一码,也叫万国码、单一码,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode
是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode
用数字0 ~ 0x10FFFF
来映射这些字符,最多可以容纳1114112
个字符,或者说有1114112
个码位。
3.1 Unicode 和 ISO 10646的关系
\quad 历史上存在两个独立的尝试创立单一字符集的组织,即:
- 国际标准化组织(
ISO
)于1984
年创建的ISO/IEC JTC1/SC2/WG2
:ISO
为国际化标准组织(International Organization for Standardization
);IEC
为联合国下属的国际电工委员会(International Electro-technical Commission
);JTC1
则是由ISO
及IEC
双方所协议共组的第一联合技术委员会(Join Technical Committee One
),负责制订与资讯处理、资讯技术相关的国际标准;ISO/IEC JTC1/SC2
为设于JTC1
之下的第二分组委员会(Sub-Committee Two
),负责把各种语言符号集的内码标准化;而ISO/IEC JTC1/SC2/WG2
则是ISO/IEC JTC1/SC2
之下的第二工作组(Working Group Two
),专门负责ISO/IEC 10646
《信息技术通用多八位编码字符集(Universal Multiple-Octet Coded Character Set
,简称UCS
)》的研制工作。ISO/IEC10646
是ISO/IEC646
的扩展(ISO/IEC 646
是国际标准化组织(ISO
)和国际电工委员会(IEC
)于1972
年制订的标准,它是一个7-bit
字符的字集,来自数个国家标准,最主要来自美国的ASCII
)。
- 由
Adobe
、Xerox
、Apple
、IBM
、微软等电脑软硬件制造商于1988年组成的Unicode
(统一码)联盟。
\quad
20
世纪80
年代末,组成Unicode
联盟的商业机构和国际标准化组织(ISO
)在计算机普及和信息国际化的背景之下,分别各自成立了Unicode
联盟和ISO-10646
工作小组,主要解决传统字符编码方案的局限:无法同时支持多语言环境。他们双方在不久之后便发现了对方的存在,大家为着相同的目的而工作,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。1991
年,Unicode
联盟与ISO/IEC JTC1/SC2
同意保持Unicode
编码表与ISO 10646
标准兼容并密切协调各自标准的进一步扩展。两者虽然为两个不同的标准,但是实际上两者的字集编码完全相同。例:Unicode 1.1
对应于ISO 10646-1:1993
,Unicode 3.0
对应于ISO 10646-1:2000
,Unicode 3.2
对应于ISO 10646-2:2001
,Unicode 4.0
对应于ISO 10646:2003
,Unicode 6.1
对应于ISO/IEC 10646:2012
,Unicode 13.0
对应于ISO 10646:2020
3.3 Unicode 字符平面映射
\quad
当Unicode
字符集刚问世的时候,是采取16-bit
编码的,也就是
2
16
2^{16}
216个码位,可以表示65536
个字符。但是随着Unicode里面收录的字符越来越多,
2
16
2^{16}
216个码位已经不能满足需求,于是从Unicode 3.1
版本开始,Unicode
引入了平面(Plane
)的概念,用一个额外的byte
来标识平面号。从此,Unicode
的表示方式是:U+hhhhhh
,h
表示0
~F
的十六进制数。
\quad
既然有一个单独的字节来表示平面号,理论上我们可以最多有256
个不同的平面。不过,Unicode
仅将字符分成了17
个平面(目前只用了少数平面),而每平面拥有65536
(即
2
16
2^{16}
216)个码点,可以收录一百多万个字符。我们将原有的Unicode
空间称为基本平面或基本多文种平面(Basic Multilingual Plane
, 简称BMP
),其余的16
个平面被称为辅助平面。在基本平面上的字符可以只使用4
位十六进制数表示,即U+hhhh
。
平面 | 始末字符值 | 中文名称 | 英文名称 |
---|---|---|---|
0 号平面 | U+0000 ~ U+FFFF | 基本多文种平面 | Basic Multilingual Plane ,简称BMP |
1 号平面 | U+10000 ~ U+1FFFF | 多文种补充平面 | Supplementary Multilingual Plane ,简称SMP |
2 号平面 | U+20000 ~ U+2FFFF | 表意文字补充平面 | Supplementary Ideographic Plane ,简称SIP |
3 号平面 | U+30000 ~ U+3FFFF | 表意文字第三平面 | Tertiary Ideographic Plane ,简称TIP |
4 号平面 ~ 13 号平面 | U+40000 ~ U+DFFFF | (尚未使用) | (尚未使用) |
14 号平面 | U+E0000 ~ U+EFFFF | 特别用途补充平面 | Supplementary Special-purpose Plane,简称 SSP` |
15 号平面 | U+F0000 ~ U+FFFFF | 保留作为私人使用区(A区) | Private Use Area-A ,简称PUA-A |
16 号平面 | U+100000 ~ U+10FFFF | 保留作为私人使用区(B区) | Private Use Area-B ,简称PUA-B |
- 基本多文种平面,或称第
0
平面或0
号平面(Plane 0
),包含了最常用的字符,共支持六万多个字符。 - 第一辅助平面又称多文种补充平面(
Plane 1
),主要摆放绝大多数古代文字,现时已不再使用或很少使用文字、速记、数学字母符号、音符、图形符号及用于学者的专业论文中使用的古老或过时的语言书写符号,以及网络通信等使用的表情符号。 - 第二辅助平面又称表意文字补充平面(
Plane 2
),整个平面配置的都是一些罕用的汉字或地区的方言用字,如粤语用字及越南语的字喃。 - 第三辅助平面已有相关编码提案。本平面现已用来摆放汉字扩展区
G
区,并规划用于摆放甲骨文、金文、小篆、中国战国时期文字等。 - 第四至第十三辅助平面尚无使用计划。
- 第十四辅助平面又称特别用途补充平面,目前仅摆放 “语言编码标签” 和 “字形变换选取器”,它们都是控制字符。
- 第十五至十六辅助平面都是私人使用区,支持自定义编码。
\quad
Unicode
是一个很大的集合,每个符号的编码都不一样,例:U+0639
表示阿拉伯字母Ain
,U+0041
表示英语的大写字母A
,U+4E25
表示汉字严
。具体的符号对应表,可以查询 https://unicode-table.com/cn/#,或者专门的 汉字对应表。
四、Unicode 的问题
\quad
需要注意的是,Unicode
只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
\quad
比如:汉字严的Unicode
是十六进制数4E25
,转换成二进制数足足有15
位(100111000100101
),也就是说,这个符号的表示至少需要2
个字节。表示其他更大的符号,可能需要3
个字节或者4
个字节,甚至更多。
\quad
这里就有两个严重的问题。第一个问题是,如何才能区别Unicode
和ASCII
?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode
统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0
,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
\quad
它们造成的结果是:(1)
出现了Unicode
的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode
。(2)Unicode
在很长一段时间内无法推广,直到互联网的出现。
五、Unicode 的存储
\quad
Unicode
字符集为每个字符分配了一个唯一的编号,通过这个编号就能找到对应的字符。在编程过程中我们经常会使用字符,而使用字符的前提就是把字符放入内存中,毫无疑问,放入内存中的仅仅是字符的编号,而不是真正的字符实体。
\quad 这就抛出了一个问题,如何才能将字符编号放入内存中呢?
\quad
对于ASCII
字符集,这很容易。ASCII
总共包含128
个字符,用7
个比特位(Bit
)恰好能够存储,不过考虑到计算机一般把字节(Byte
)作为基本单元,为了操作方便,我们不妨用一个字节(也就是8
个比特位)来存储ASCII
。这样虽然浪费了一个比特位,但是读写效率提高了。
\quad
但是对于Unicode
,问题就没有这么简单了。Unicode
目前已经包含了上百万的字符,位置靠前的字符用一个字节就能存储,位置靠后的字符用三个字节才能存储。我们可以为所有字符都分配三个字节的内存,也可以为编号小的字符分配一个字节或者两个字节的内存,而为编号大的字符分配三个字节的内存。
\quad 这两种方案各有优缺点,请读者看下面的分析。
5.1 方案1:为每个字符分配固定长度的内存
\quad
方案1
是为每个字符分配固定长度的内存,并且这块内存要足够大,可以容纳下所有的字符编号。这种方案最简单,直接将字符编号放入内存中即可,不需要任何转换,并且以后在字符串中定位字符、修改字符都非常容易。
\quad
目前的Unicode
已经收录了上百万的字符,至少需要三个字节才能容纳下所有的字符编号。假设字符串“A3中🥰
”的Unicode
编码值(十六进制形式)分别是U+0041
、U+0033
、U+4E2D
、U+1F970
,那么它们在内存中的存储形式为:
\quad
在几乎所有的字符集(包括 Unicode
),常用字符的编号往往比较小,罕见字符的编号往往比较大。
\quad
A
和3
是ASCII
编码中的字符,Unicode
为了兼容ASCII
,在设计时刻意保留了原来ASCII
中字符的编号,所以英文字母和阿拉伯数字在Unicode
中的编号都非常小,用一个字节足以容纳。中
是一个汉字,编号比较大,一般要用两个字节才能容纳。🥰可以看做是一个极其少见,或者只有极少数地区才会使用到的字符,这样的字符编号往往比较大,有时候需要三个字节才能容纳。
\quad
上图中带灰色背景的字节是没有用到的字节,它们就是被浪费掉的一部分内存空间,这就是用固定长度的内存来存储字符编号的缺点:常用字符的编号都比较小,这种方案会浪费很多内存空间,对于以英文为主的国家,比如:美国、加拿大、英国等,内存利用率甚至会低于50%
。
5.2 方案2:为每个字符分配尽量少的内存
\quad
既然上面的方案有缺点,那我们就来改进一下。改进的思路也很明确,就是把空闲的内存压缩掉,为每个字符分配尽量少的字节,例如:A
和3
分配一个字节足以,中
分配两个字节足以,如下图所示:

\quad
这样虽然没有了空闲字节,不浪费任何内存空间了,但是又出现新的问题了:如果我不告诉你,你怎么知道41
表示一个字符,而不是4133
或者41334E
才表示一个字符呢?后面的字符也有类似的问题。
\quad 对于第一种方案,每个字符占用的字节数是固定的,很容易区分各个字符;而第二种方案,不同的字符占用的字节数不同,字符之间也没有特殊的标记,计算机是无法定位字符的。
\quad 这种方案还需要改进,必须让不同的字符编码有不同的特征,并且字符处理程序也需要调整,要根据这些特征去识别不同的字符。
\quad 要想让不同的字符编码有不同的特征,可以从两个方面下手:
-
一是从字符集本身下手,在设计字符集时,刻意让不同的字符编号有不同的特征。
-
例如:对于编号较小的、用一个字节足以容纳的字符,我们就可以规定这个字符编号的最高
Bit
位必须是0
;对于编号较大的、要用两个字节存储的字符,我们就可以规定这个字符编号的高字节的最高位必须是1
,低字节的最高位必须是0
;对于编号更大的、需要三个字节存储的字符,我们就可以规定这个字符编号的所有字节的最高位都必须是1
。 -
程序在定位字符时,从前往后依次扫描,如果发现当前字节的最高位是
0
,那么就把这一个字节作为一个字符编号;如果发现当前字节的最高位是1
,那么就继续往后扫描,如果后续字节的最高位是0
,那么就把这两个字节作为一个字符编号;如果后续字节的最高位是1
,那么就把挨着的三个字节作为一个字符编号。
\quad 这种方案的缺点很明显,它会导致字符集不连续,中间留出大量空白区域,这些空白区域不能定义任何字符。
-
-
二是从字符编号下手,可以设计一种转换方案,字符编号在存储之前先转换为有特征的、容易定位的编号,读取时再按照相反的过程转换成字符本来的编号。
\quad 那么,转换后的编号要具备什么样的特征呢?其实也可以像上面一样,根据字节的最高位是
0
还是1
来判断字符到底占用了几个字节。\quad 相比第一种方案,这种方案有缺点也有优点:
- 缺点就是多了转换过程,字符在存储和读取时要经过转换,效率低;
- 优点就是在制定字符集时不用考虑存储的问题,可以任意排布字符。
5.3 Unicode 到底使用哪种编码方案?
\quad
Unicode
是一个独立的字符集,它并不是和编码绑定的,你可以采用第一种方案,为每个字符分配固定长度的内存,也可以采用第二种方案,为每个字符分配尽量少的内存。
\quad
需要注意的是,Unicode
只是一个字符集,在制定的时候并没有考虑编码的问题,所以采用第二种方案时,就不能从字符集本身下手了,只能从字符编号下手,这样在存储和读取时都要进行适当的转换。
- Unicode 可以使用的编码有三种,分别是:
UTF-8
:一种变长的编码方案,使用1 ~ 4
个字节来存储;UTF-32
:一种固定长度的编码方案,不管字符编号大小,始终使用4
个字节来存储;UTF-16
:介于UTF-8
和UTF-32
之间,使用2
个或者4
个字节来存储,长度既固定又可变。
\quad
UTF
是Unicode Transformation Format
的缩写,意思是“Unicode
转换格式”,后面的数字表明至少使用多少个比特位(Bit
)来存储字符。
5.3.1 UTF-8
\quad
随着互联网的普及,强烈要求出现一种统一的编码方式。UTF-8
就是在互联网上使用最广的一种Unicode
的实现方式。其他实现方式还包括UTF-16
(字符用两个字节或四个字节表示)和UTF-32
(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8
是Unicode
的实现方式之一。
\quad
UTF-8
最大的一个特点,就是它是一种变长的编码方式。它可以使用1 ~ 4
个字节表示一个符号,根据不同的符号而变化字节长度。
\quad
UTF-8
的编码规则很简单:如果只有一个字节,那么最高的比特位为0
;如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1
,就使用几个字节编码,剩下的字节均以10
开头。
\quad
具体的表现形式为:
\qquad
0xxxxxxx
:单字节编码形式,这和ASCII
编码完全一样,因此UTF-8
是兼容ASCII
的;
\qquad
110xxxxx 10xxxxxx
:双字节编码形式;
\qquad
1110xxxx 10xxxxxx 10xxxxxx
:三字节编码形式;
\qquad
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
:四字节编码形式。
\quad
下面是一些字符的编码实例(红色部分表示本来的Unicode
编号):
\quad
对于常用的字符,它的Unicode
编号范围是0 ~ FFFF
,用1 ~ 3
个字节足以存储,只有及其罕见,或者只有少数地区使用的字符才需要4
个字节存储。查看字符编码(UTF-8
):http://www.mytju.com/classcode/tools/encode_utf8.asp
5.3.2 UTF-32
\quad
UTF-32
是固定长度的编码,始终占用4
个字节,足以容纳所有的Unicode
字符,所以直接存储Unicode
编号即可,不需要任何编码转换。浪费了空间,提高了效率。
5.3.3 UTF-16
\quad
UFT-16
比较奇葩,它使用2
个或者4
个字节来存储。
\quad
对于Unicode
编号范围在0 ~ FFFF (00000000 00000000 ~ 11111111 11111111)
之间的字符,UTF-16
使用两个字节存储,并且直接存储Unicode
编号,不用进行编码转换,这跟UTF-32
非常类似。
\quad
对于Unicode
编号范围在10000~10FFFF (00000000 00000001 00000000 00000000 ~ 00000000 00010000 11111111 11111111)
之间的字符,UTF-16
使用四个字节存储。
具体的编码规则如下:
- 对于
Unicode
码小于0x10000
的字符,使用2
个字节存储,并且是直接存储Unicode
码,不用进行编码转换。 - 对于
Unicode
码在0x10000
和0x10FFFF
之间的字符,使用4
个字节存储,这4
个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前6
位二进制固定为110110
,后面两个字节的前6
位二进制固定为110111
,其余部分使用字符对应的Unicode
码减去0x10000
的结果填充。 - 大于
0x10FFFF
的Unicode
码无法使用UTF-16
编码。
示例: 下面以这个 𑄄 查克瑪 字母 I. 为例来说明UTF-16
编码4
字节存储。

\quad
𑄄
字符的Unicode
码为U+11104
,转为二进制为00 01000100 01 00000100
,减去0x10000 (00 01000000 00 00000000)
得00 00000100 01 00000100
,按照填充规则得到
11011000
00000100
11011101
00000100
\pmb{110110}00\ 00000100\ \pmb{110111}01\ 00000100
11011011011011011000 00000100 11011111011111011101 00000100,转换成十六进制则为D804DD04
,所以𑄄
字符的UTF-16
编码为:
5.4 UTF 字节序
\quad
编码单元(Code unit
):最小的比特位组合,表示用于交换或处理的编码文本单元。Unicode
标准中定义,UTF-8
使用8
比特的编码单元,UTF-16
使用16
比特的编码单元,UTF-32
使用32
比特的编码单元。
\quad
编码单元是多字节的才会有字节序的问题存在,UTF-8
最小编码单元是一字节,所以它是没有字节序的问题;UTF-16
最小编码单元是2
个字节,在解析一个UTF-16
字符之前,需要知道每个编码单元的字节序。比如:前面提到过,“中
”字的Unicode
码是4E2D
,而“?
”字符的Unicode
码是2D4E
,当我们收到一个UTF-16
字节流4E2D
时,计算机如何识别它表示的是字符“中
”还是 字符“?
”呢 ?
\quad
所以,对于多字节的编码单元,需要有一个标记显式的告诉计算机,按照什么样的顺序解析字符,也就是字节序,字节序分为大端字节序(BE
,Big-Endian
)和小端字节序(LE
,Little-Endian
)。
- 大端字节序表示高位字节在前,低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端。这是人类读写数值的方法。
- 小端字节序表示低位字节在前,高位字节在后,高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端。小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为
CPU
读取内存中的数据时,是从低地址向高地址方向进行读取的。
\quad
下面以“中
”字的Unicode
码0x4E2D
为例来说明大端和小端:
5.5 BOM
\quad
BOM
是Byte Order Mark
的缩写,是“字节序标记”的意思, 它常被用来标识文件是以UTF-8
、UTF-16
或UTF-32
编码的。
\quad
在UCS
(Universal Character Set
)编码,即Unicode
编码,中有一个叫做 “Zero Width No-Break Space
”,中文译名作 “零宽无间断间隔”的字符,它的编码是FEFF
。而FEFF
在UCS
中是不存在的字符(只有编码没有实际的符号),所以不应该出现在实际传输中。UCS
规范建议我们在传输字节流前,先传输字符 “Zero Width No-Break Space
”。这样如果接收者收到FEFF
,就表明这个字节流是Big-Endian
的;如果收到FFFE
,就表明这个字节流是Little- Endian
的。因此字符 “Zero Width No-Break Space
”(“零宽无间断间隔”)又被称作BOM
。
\quad
对于UTF-16
,如果接收到以FE FF
开头的字节流,就表明是大端字节序,如果接收到FF FE
,就表明字节流是小端字节序。
\quad
对于UTF-32
,如果接收到以00 00 FE FF
开头的字节流,就表明是大端字节序,如果接收到FF FE 00 00
,就表明字节流是小端字节序。
\quad
UTF-8
不需要BOM
来表明字节顺序,但可以用BOM
来表明编码方式。字符 “Zero Width No-Break Space
” 的UTF-8
编码是EF BB BF
。所以如果接收者收到以EF BB BF
开头的字节流,就知道这是UTF-8
编码了。