一文读懂字符编码(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)。
  • 19668月,又改组为美利坚合众国标准学会USASI)。
  • 1969106日改成现名:美国国家标准学会(ANSI)。

\quad ANSI是非赢利性质的民间标准化团体,但它实际上已成为国家标准化中心。美国国家标准局(NBS)的工作人员和美国政府的其他许多机构的官方代表也通过各种途径来参与美国国家标准学会的工作。

\quad ANSI是国际标准化组织(ISO)和国际电工委员会(IEC5个常任理事成员之一,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年开始,ANSIECMA合作使用单字节ASCII编码以外的区域,存储及表示使用拉丁字母的语言、使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等。1985年,正式公布了ECMA-94标准,即后来的ISO/IEC 8859 parts 1, 2, 3, 4。第5678910111213141516部分分别公布于1988年、1987年、1987年、1987年、1989年、1992年、2001年、1997年(正式宣布放弃研发)、1998年、1998年、1999年、2001年。

1.3 ISO/IEC

\quad ISO为国际标准化组织(International Organization for Standardization),是目前世界上最大、最有权威性的国际标准化专门机构。国际标准化组织的前身是国家标准化协会国际联合会联合国标准协调委员会194610月,25个国家标准化机构的代表在伦敦召开大会,决定成立新的国际标准化组织,定名为ISO1947223日,国际标准化组织正式成立。

\quad IEC为联合国下属的国际电工委员会(International Electro-technical Commission),成立于1906年,它是世界上成立最早的国际性电工标准化机构,负责有关电气工程电子工程领域中的国际标准化工作。国际电工委员会的总部最初位于伦敦,1948年搬到了位于日内瓦的现总部处。1887 ~ 1900年召开的6次国际电工会议上,与会专家一致认为有必要建立一个永久性的国际电工标准化机构,以解决用电安全和电工产品标准化问题。1904年在美国圣路易召开的国际电工会议上通过了关于建立永久性机构的决议。19066月,13个国家的代表集会伦敦,起草了IEC章程和议事规则,正式成立了国际电工委员会。1947年作为一个电工部门并入国际标准化组织(ISO),1976年又从ISO中分立出来。

\quad ISO联合IEC制定标准,并且将一些现有的标准纳入国际标准,例:ASCII对应ISO/IEC 646ECMA-35对应ISO/IEC 2022ECMA-94对应ISO/IEC 8859-1 ~ ISO/IEC 8859-4

1.4 统一码联盟

\quad 统一码联盟(The Unicode Consortium)是统筹Unicode发展的非营利组织,其宗旨为最终以Unicode取代现存的字符编码,因为现存编码不能够在多语言电脑环境中使用,而且字符数有局限。同时它也制定了数种Unicode转换格式(Unicode Transformation FormatUTF)。Unicode的成功让电脑使用进入了一个新纪元,并应用于很多新技术,如XMLJava编程语言和现今的操作系统。

\quad 统一码联盟有来自多个国家政府和各大软件商的代表参与。统一码联盟积极与各标准制订机构合作,包括国际标准化组织(ISO)、国际电工委员会(IEC)、万维网联盟(W3C)、互联网工程工作小组(IETF)和Ecma国际等。

二、单字节字符系统

\quad 单字节字符系统(Single-Byte Character SystemSBCS),我们可以从名称知道该系统的字符可以只用78比特表示,换句换说,系统包含最多128256个字符。例:‘ASCII’和‘ISO 8859’,都是世界上最流行的单字节字符系统,还有专门用于日文的‘JIS X 0201’。

2.1 ASCII 字符集 & 字符编码

2.1.1ASCII 字符集

\quad 1960年代初期,美国国会图书馆(Library of Congress,简称LC)的Henriette Avram等人开始研拟机读编目格式。同时,James Agenboard等人为此开始制订英文的字符集与交换码,作为图书馆界书目交换的共同标准。LC交换码遂成为美国信息交换标准码ASCIIAmerican 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)有01两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000011111111

\quad 上个世纪60年代,美国制定了一套字符编码规则,对英语字符与二进制位之间的关系做了统一规定,这编码规则被称为ASCII编码,一直沿用至今。ASCII字符编码规定了将ASCII字符集转换为计算机可接受的二进制数字系统的规则:使用一个字节的后7位(bits)表示一个字符,共128字符,最前面的一位(最高位)统一规定为0,其余位置将字符编号相应的转成二进制数字进行存储即可。例:空格SPACEASCII字符编码是00100000,大写的字母AASCII字符编码是01000001

\quad ASCII编码的最大缺点是只能显示26个基本拉丁字母、阿拉伯数字和英式标点符号,因此它只能用于显示现代美国英语(而且在处理英语当中的外来词如naïvecaféélite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。

\quad 早期一般认为字符集和字符编码是同义词,并不需要进行严格区分。因为在传统字符编码模型中,基本上都是将字符集里的字符进行编号(字符编号转化为二进制数后一般不超过一个字节),然后该字符编号就是字符的编码。因此在像ASCII这样的简单字符集为代表的传统字符编码模型中,这两个概念的含义几乎是等同的。

2.2 ISO 8859 字符集 & 字符编码

\quad 随着很多欧洲国家也开始使用计算机,人们发现使用128个编号可以将英文符号一一对应,但是用来表示其他语言,128个编号是不够的,例:在法语中,字母上方有注音符号,它就无法用ASCII字符集表示。人们就在想,单字节能够表示的数字(编号)有256个,而ASCII字符只使用了前128个,后面128个数字还没有被使用。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号,例:法语中的é的编码为130。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。这个表示方式被称为EASCIIExtended ASCII)。

\quad 但是,这里又出现了新的问题:不同的国家有不同的字母。因此,哪怕欧洲各国都使用256个符号的编码方式,代表的字母却不一样,比如:130在法语字符集中代表了é;在希伯来语字符集中却代表了字母Gimel (ג);在俄语字符集中又会代表另一个符号。但是不管怎样,在所有这些字符集中,编号0 ~ 127表示的符号是一样的,不一样的只是编号128 ~ 255的这一段。

\quad 因为在EASCII中表示的256个字符中,前128字符和ASCII字符集表示的字符完全一样,后128个字符欧洲各国或地区都有自己的字符集,所以为了统一各国各语言单独编码的混乱局面,欧洲计算机制造商协会(Ecma国际)在上世纪80年代中期开始陆续公布了ECMA-94标准,之后被国际标准化组织收纳并命名为ISO 8859ISO8859不是单个标准,而是一系列的子标准,这些子标准适用于欧洲不同的国家地区,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:1999Cyrillic,收集了斯拉夫语系字符。(曾推出过ISO 8859-5:1988版)
  • ISO8859-6字符集,正式编号为ISO/IEC 8859-6:1999Arabic,收集了阿拉伯语系字符。(曾推出过ISO 8859-6:1987版)
  • ISO8859-7字符集,正式编号为ISO/IEC 8859-7:2003Greek,收集了希腊字符。(曾推出过ISO 8859-7:1987版)
  • ISO8859-8字符集,正式编号为ISO/IEC 8859-8:1999Hebrew,收集了西伯莱(犹太人)字符。(曾推出过ISO 8859-8:1988版)
  • ISO8859-9字符集,正式编号为ISO/IEC 8859-9:1999,又称为Latin-5Turkish,它把Latin-1的冰岛语系字符换走,加入土耳其语系字符。(曾推出过ISO/IEC 8859-9:1989版)
  • ISO8859-10字符集,正式编号为ISO/IEC 8859-10:1998,又称为Latin-6Nordic,收集了北欧(主要指斯堪地那维亚半岛)的字符,用来代替Latin-4。(曾推出过ISO/IEC 8859-10:1992版)
  • ISO8859-11字符集,正式编号为ISO/IEC 8859-11:2001Thai,它是从泰国的TIS620标准字符集演化而来。
  • ISO8859-12字符集,目前尚未定义。
  • ISO8859-13字符集,正式编号为ISO/IEC 8859-13:1998,又称为Latin-7Baltic Rim,主要涵盖波罗的海(Baltic)诸国的文字符号,也补充一些在Latin-6中遗漏的拉脱维亚(Latvian)字符。
  • ISO8859-14字符集,正式编号为ISO/IEC 8859-14:1998,又称为Latin-8Celtic,它将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 SystemMBCS),相对于主要用于拉丁语系国家的SBCS来说,MBCS主要是为拥有更多字符需要表示的其他国家服务,例:中国、日本等。显然,1个字节不足以表示那么多的字符。

3.1 ISO/IEC 2022 通用技术规范

\quad ISO 2022,全称ISO/IEC 2022,由国际标准化组织(ISO)及国际电工委员会(IEC)联合制定,是一个使用7位或8位编码表示各种语言文字的通用技术规范。ISO 2022既不是字符集,也不是字符编码,而是一种标准或者说代码扩展规定,包括:多重字符集的编码,以及切换字符集与字符编码的方式。在ISO/IEC 2022的延伸编码结构中,可按照字符集的大小选定其字符编码为单字节或多字节,而每一个字节则可选择为7bit8bit。中国国标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图形字符所组成),并且每字节的最高位必须都为10

\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)个图形字符,因为要求每字节的最高位都为10,只有两个选择,可以看成(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规定字符集的控制字符可分为两块:C0C1;图形字符可分为四块:G0G1G2G3。对于单字节7-bit编码, 字节值\0x00(0) ~ \0x1F(31) 保留给C0控制字符块,字节值\0x20(32) ~ \0x7F(127) 用于G0G1G2G3字符块;对于双字节7-bit编码的字符集,1个图形字符块可包含94 x 94个字符,可以使用控制符的转义序列来表示在G0G1G2G3之间的切换。

\quad 8-bit字符编码时,\0x00 ~ \0x1F表示C0或称CL区(Lleft缩写,因为其在字符表的左侧),\0x80 ~ \0x9F表示C1或称CRRRight缩写,因为其在字符表的右侧)。\0x20 ~ \0x7F表示G0(称GL区),\0xA0 ~ \0xFF(称GR区)可表示G1, G2, G3。(默认GL区指G0字符,GR区指G1字符)。如下表所示:
在这里插入图片描述
\quad ISO/IEC 2022的图形字符编码结构,由三部分构成,如图所示:

  • 现用字符集区:收容让电脑据此处理图形字符编码的字符集,除非字符编码之前附加调用控制符,否则电脑会将所读取的任何图形字符编码都当做现用字符集里的某个字符而加以处理或显示。在7-bit环境下,现用字符集区只有单一的编码空间。在8-bit环境下,现用字符集区则区分成左、右两个编码子空间。
  • 备用字符集区:收容最多四个备用的图形字符集,分别称为G0G1G2G3
  • 图形字符集库:收容电脑处理文字、数字信息所需的全部图形字符集,其数量可多达上千个。这些字符集的字符编码,可以是单字节或多字节,每个字节可以是7-bit8-bit,详情如前文所述。

\quad ISO/IEC 2022规定电脑必须先利用逃逸序列(escape sequence)控制符从图形字符集库中将最多四个图形字符集分别载入备用字符集区。然后再借由调用(invocation)控制符,从四个备用字符集G0G1G2G3当中挑选其一,载入现用字符集区,让电脑可据以处理信息或显示字符图形。每个图形字符集及字符编码都必须赋予特定的逃逸序列控制符,并需先向ISO登记注册,以便不同电脑之间能有共同遵循的依据,而不至于将字符编码对应到错误的字符集。但是,如果许多电脑共同使用不超过四个的图形字符集,则可经由彼此约定,将这些字符集分别预设为G0G1G2G3,因而可省略逃逸序列控制符。

\quad ISO/IEC 2022供单字节图形字符编码选用的三字节逃逸序列控制符,以及供多字节图形字符编码选用的四字节逃逸序列控制符如下表所示。这些逃逸序列控制符里的终结字符[F],其字符编码依规定必须是从\0x30\0x7E7-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 一旦使用对应的锁定移位调用控制符将四个备用字符集(G0G1G2G3之一载入到现用字符集区之后,就会成为常驻字符集。若要将现用常驻字符集切换为另一个备用字符集,必须再借助对应的锁定移位调用控制符,重新将目标字符集载入现用常驻字符集区。至于单一移位(single-shift)调用控制符则只对紧跟其后的一个字符编码发生作用。换言之,当电脑遇有单一移位调用控制符时,就会将紧跟于控制符之后的一个字符编码,当做该调用控制符所指定的备用字符集里的某个字符而加以处理或显示。但是,下一个字符编码则恢复为适用现用常驻字符集。

\quad ISO 8859-X字符集是特定的把ISO 2022的若干成分组合起来的字符集。这些成分包括:低端控制字符(C0)、空格字符及删除字符(34个);ASCII字符集(GL94个);高端控制字符(C132个);高端字符(GR96个)是特定于每个ISO 8859-X变种的字符,例:ISO-8859-1是由ISO-IR-1ISO-IR-6ISO-IR-77ISO-IR-100组成。

\quad 对于GB 2312,是双字节8-bit编码。其汉字编码空间为94 x 94,即有94个区,每个区有94个位(用来编码字符)。实际使用了16 ~ 55区编码一级汉字,56 ~ 87区编码二级汉字。这些汉字均放在了GR字符块区,可表示G1, G2, G3。字节值在\0x00 ~ \0x7F为单字节表示一个字符,构成了C0G0区,与ASCII码兼容。因此,GB 2312是单、双字节混合编码。

  • 注:GBK编码作为Windows操作系统(简体中文版)的缺省语言locale设置,GBK编码虽然完全向后兼容GB 2312,但GBK突破了ISO 2022GR区域的字数限制:94 x 94 = 8,836个字。

\quad ISO 2022使用“转义序列”(Escape sequence)指出随后的字符属于哪个字符集。这些字符集在ISO登记,并遵循ISO 2022标准规定的模式。

代码Hex缩写名称效果
ESC ! A1B 21CZDC0-designate使用C0控制符
ESC “ A1B 22C1DC1-designate使用C1控制符
ESC %1B 25DOCSDesignate other coding system8比特编码;使用ESC % @重置回ISO 2022
ESC % /1B 25 2FDOCSDesignate other coding system8比特编码;无法重置

3.2 ISO/IEC 10646

\quad 随着计算机功能的日趋强大与价格的日趋便宜,其应用领域也越来越广。但是随之而来的各种编码需求,却使得单一字节的编码方式,因编码空间太小,变得不足以因应各种应用程序的需求。中文字、排版系统的标志符号、非英语拼音字母和图形符号等的编码,需要使用2个或多个字节来编码。同时,为了预防这些多字节字符码被计算机或网络设备“吃掉”其中的某个字节,编码时必须避开每个字节的03212734个字节值,这种做法严重浪费编码空间。

\quad 19844月,国际标准化组织(ISO)的一些会员国发起制定新的国际计算机字符的编码标准,并成立了工作小组(ISO/IEC JTC1/SC2/WG2),针对各国文字符号进行统一性编码的研发工作,最后定案的标准名为Universal Multiple-Octet Coded Character Set(简称UCS),编号则订为ISO/IEC 10646,进一步成为全球计算机字符的编码标准。

3.3 GB 系列字符集 & 字符编码

  • 前言一段历史
    • 1978年,日本基于ISO 2022制订了全世界最早的汉字编码JIS C 62261980年代,中国大陆、中国台湾、韩国各自制订了自己的规范。这些规范彼此之间并无关系。若要在一份文件中同时使用,则要以转义字符的方式来交换。
    • 1980年,日本的国立国会图书馆的高桥德太郎以图书学的观点指出,一个统一的东亚汉字编码系统是有必要的。同年,中国台湾颁布了第一版三字节存储的中文资讯交换码(Chinese Character Code for Information InterchangeCCCII),这是第一个期望可以一致处理中国、日本、韩国汉字的编码。之后,美国的国会图书馆采用了此标准,并另外命名为东亚编码字符(East Asian Character CodeEACCANSI/NISO Z39.64)。
    • 19844月,国际标准化组织(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多个,之前欧美定义的使用单字节表示一个字符已经不能够表示汉字,于是人们想到了使用双字节来表示汉字,并且将这种表示方式命名为GB2312GBGuóbiāo国标,中华人民共和国国家标准,简称国标,即国标2312)。GB2312字符集遵循ISO/IEC 2022标准,将所有字符放在94x94的格子中,也被称为区位码。将字符所在的区块称为区,区中的位置称为位。

\quad GB2312GB2312-80是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集-基本集》,又称GB0,由中国国家标准总局发布,198151日实施。GB2312字符集通行于中国大陆;新加坡等地也采用此字符集。中国大陆几乎所有的中文系统和国际化的软件都支持GB2312GB2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。

\quad GB2312共收录有7445个字符,其中简体汉字6763个:包括一级(常用)汉字3755个,二级(次常用)汉字3008个。同时,GB2312编码收录了包括拉丁字符、希腊字符、日文平假名及片假名字符、俄语西里尔字符在内的682个全角字符。GB2312字符集针对ASCII字符集中原有的数字、标点、字母,重新赋予了两个字节长的编号,这就是常说的"全角"字符,而ASCII字符集中原有的字符被称为"半角"字符。

\quad GB2312将所收录的字符分为94个区,编号为01区至94区;每个区收录94个字符,编号为01位至94位。GB2312的每一个字符都由与其唯一对应的区号和位号(即码位)所确定,因此字符所在位置也被称为区位码。GB2312字符集各区详解:

分区号字符数字符类别
0194一般符号
0272顺序号码
0394拉丁字母
0483日文平假名
0586日文片假名
0648希腊字母
0766俄语西里尔字母
0863汉语拼音和注音符号
0976图形符号
10 ~ 15无字符备用区
16-553755个一级(常用)汉字,按拼音排序
56-873008个二级(次常用)汉字,按部首/笔画排序
88 ~ 94无字符备用区

\quad 区位码取值范围:0101 ~ 9494。例:“啊”字在1601位,所以“啊”字的区位码是: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+3282+32)=(77114),十六进制为:(4D72)。

  • 有人可能会问:ASCII码中还有一个不可显示字符,编号127的删除控制符,为什么没有考虑它?这是因为 “区码” 和 “位码” 最大数值都是94,加上32后是126,正好没有到127,所以无须考虑它。

(2)机内码(内码)

\quad 人们使用国标码解决了字符的显示问题,可是在计算机的使用过程中,出现了一个新的问题:使用GB2312国标码将简体中文字符编码到二进制数字进行传输后,对方解码出的文字并不是我们要传输的内容,这就发生了乱码问题。

\quad 发生此问题的原因是GB2312使用双字节存储简体中文字符,而我们使用区位码进行编码的这一过程中不会发生问题,在我们解码过程中,“区码” 和 “位码” 的取值范围与ASCII码单字节的英文字母、数字和符号等可打印字符取值范围恰好相同,因此就会产生乱码。例: “万”字的国标码为:\0x4D72,区码\0x4DM冲突,位码\0x72r冲突。由此可见,国标码并不完全兼容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 不同的国家和地区制定了不同的标准,由此产生了GB2312GBKGB18030Big5Shift_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(代码页)是字符编码的别名,也称内码表,是特定语言的字符集的一张表,简称为CPIBM和微软内部使用数字来标记不同的编码字符集,不同的厂商对同一个字符集编码使用各自不同的名称。例:UTF-8IBM称作CP1208,在微软称作CP65001,在SAP称作CP4110

3.4.1 OEM 代码页

\quad 早期,Code PageIBM公司称呼计算机的BIOS所支持的字符集编码。当时通用的操作系统都是命令行界面,这些操作系统直接使用BIOS提供的字符绘制功能来显示字符(或者是一组嵌入在显卡字符生成器中的字形),这些BIOS代码页也被称为OEM代码页(或硬件代码页)。
\quad 19874月,IBM发布了PC-DOS 3.3,微软发布了几乎完全相同的MS-DOS 3.3,正式向普通PC用户引入了Code Page(代码页)这个概念,并且允许用户在操作系统中使用Code Page指令设置字符编码。这时的PC机使用CGAColor Graphics Adaptor,彩色图形适配器,IBM公司的第一个彩色图形卡)显示系统的字符界面,绘制不同语言的字符需要依靠BIOS硬件厂商(在当时就是指制定业界标准的IBM)提供的功能。如果想更换操作系统所支持的字符集,就必须换上支持该字符集的硬件设备。微软作为DOS操作系统的软件厂商,并不拥有绘制这些字符集的知识产权。最常见、最具代表性的OEM代码页是 “IBM PCMS-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系列的第一个产品,于19851120日开始发行。Windows NTWindows 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中的字符(编码方式不同),GBKGB2312-80具有向后兼容(向下兼容)性。

\quad MicrosoftWindows951995824日正式发布)和Windows NT 3.5119955月发布)中实现了对GBK编码的支持,虽然GBK并不是官方标准,但Windows 95的广泛使用使得GBK成为了“事实标准”。于是,由中华人民共和国全国信息技术标准化技术委员会1995121制订,国家技术监督局标准化司和电子工业部科技与质量监督司19951215日联合以《技术标函[1995]229号》文件的形式公布了《汉字内码扩展规范》版本1.0Chinese Internal Code Specification,即GBK 1.0)。 GBK共收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。GBK并非国家正式标准,只是国家技术监督局标准化司、电子工业部科技与质量监督司发布的 “技术规范指导性文件”。

\quad GBK字符集向下完全兼容GB2312-80字符集且收录了GB13000.1-1993中的全部字符,但编码方式完全不同。GBK支持GB2312-80中不支持的部分中文姓、中文繁体、日文假名,还包括希腊字母和俄语字母等字符,不过并不支持韩语字符,这也是它在实际使用中与unicode编码相比欠缺的部分。向上兼容(向前兼容) ISO 10646-1Unicode)国际标准,是GB2312ISO 10646-1Unicode)过渡过程中的一个承上启下的产物。

3.5.2 GBK 编码方式

\quad GBK亦采用双字节表示,总体编码范围:\0x8140 ~ \0xFEFE(剔除\0x**7F),首字节在81 ~ FE之间,尾字节在40 ~ FE之间(不含\0x7F),总计23940个码位,共收入21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。全部编码分为三大部分:

  1. 汉字区,包括:
    • 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-8utf-16utf-32等具体的实现,他们都对应不同的编码规则。Unicode被译为统一码,也叫万国码、单一码,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode用数字0 ~ 0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。

3.1 Unicode 和 ISO 10646的关系

\quad 历史上存在两个独立的尝试创立单一字符集的组织,即:

  1. 国际标准化组织(ISO)于1984年创建的ISO/IEC JTC1/SC2/WG2ISO为国际化标准组织(International Organization for Standardization);IEC为联合国下属的国际电工委员会(International Electro-technical Commission);JTC1则是由ISOIEC双方所协议共组的第一联合技术委员会(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/IEC10646ISO/IEC646的扩展(ISO/IEC 646是国际标准化组织(ISO)和国际电工委员会(IEC)于1972年制订的标准,它是一个7-bit字符的字集,来自数个国家标准,最主要来自美国的ASCII)。
  2. AdobeXeroxAppleIBM、微软等电脑软硬件制造商于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:1993Unicode 3.0对应于ISO 10646-1:2000Unicode 3.2对应于ISO 10646-2:2001Unicode 4.0对应于ISO 10646:2003Unicode 6.1对应于ISO/IEC 10646:2012Unicode 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+hhhhhhh表示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表示阿拉伯字母AinU+0041表示英语的大写字母AU+4E25表示汉字。具体的符号对应表,可以查询 https://unicode-table.com/cn/#,或者专门的 汉字对应表

四、Unicode 的问题

\quad 需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

\quad 比如:汉字严的Unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

\quad 这里就有两个严重的问题。第一个问题是,如何才能区别UnicodeASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果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+0041U+0033U+4E2DU+1F970,那么它们在内存中的存储形式为:
在这里插入图片描述
\quad 在几乎所有的字符集(包括 Unicode),常用字符的编号往往比较小,罕见字符的编号往往比较大。

\quad A3ASCII编码中的字符,Unicode为了兼容ASCII,在设计时刻意保留了原来ASCII中字符的编号,所以英文字母和阿拉伯数字在Unicode中的编号都非常小,用一个字节足以容纳。是一个汉字,编号比较大,一般要用两个字节才能容纳。🥰可以看做是一个极其少见,或者只有极少数地区才会使用到的字符,这样的字符编号往往比较大,有时候需要三个字节才能容纳。

\quad 上图中带灰色背景的字节是没有用到的字节,它们就是被浪费掉的一部分内存空间,这就是用固定长度的内存来存储字符编号的缺点:常用字符的编号都比较小这种方案会浪费很多内存空间对于以英文为主的国家,比如:美国、加拿大、英国等,内存利用率甚至会低于50%

5.2 方案2:为每个字符分配尽量少的内存

\quad 既然上面的方案有缺点,那我们就来改进一下。改进的思路也很明确,就是把空闲的内存压缩掉,为每个字符分配尽量少的字节,例如:A3分配一个字节足以,分配两个字节足以,如下图所示:

\quad 这样虽然没有了空闲字节,不浪费任何内存空间了,但是又出现新的问题了:如果我不告诉你,你怎么知道41表示一个字符,而不是4133或者41334E才表示一个字符呢?后面的字符也有类似的问题。

\quad 对于第一种方案,每个字符占用的字节数是固定的,很容易区分各个字符;而第二种方案,不同的字符占用的字节数不同,字符之间也没有特殊的标记,计算机是无法定位字符的。

\quad 这种方案还需要改进,必须让不同的字符编码有不同的特征,并且字符处理程序也需要调整,要根据这些特征去识别不同的字符。

\quad 要想让不同的字符编码有不同的特征,可以从两个方面下手:

  1. 一是从字符集本身下手,在设计字符集时,刻意让不同的字符编号有不同的特征。

    • 例如:对于编号较小的、用一个字节足以容纳的字符,我们就可以规定这个字符编号的最高Bit位必须是0;对于编号较大的、要用两个字节存储的字符,我们就可以规定这个字符编号的高字节的最高位必须是1,低字节的最高位必须是0;对于编号更大的、需要三个字节存储的字符,我们就可以规定这个字符编号的所有字节的最高位都必须是1

    • 程序在定位字符时,从前往后依次扫描,如果发现当前字节的最高位是0,那么就把这一个字节作为一个字符编号;如果发现当前字节的最高位是1,那么就继续往后扫描,如果后续字节的最高位是0,那么就把这两个字节作为一个字符编号;如果后续字节的最高位是1,那么就把挨着的三个字节作为一个字符编号。

    \quad 这种方案的缺点很明显它会导致字符集不连续中间留出大量空白区域这些空白区域不能定义任何字符

  2. 二是从字符编号下手,可以设计一种转换方案,字符编号在存储之前先转换为有特征的、容易定位的编号,读取时再按照相反的过程转换成字符本来的编号。

    \quad 那么,转换后的编号要具备什么样的特征呢?其实也可以像上面一样,根据字节的最高位是0还是1来判断字符到底占用了几个字节。

    \quad 相比第一种方案,这种方案有缺点也有优点:

    • 缺点就是多了转换过程,字符在存储和读取时要经过转换,效率低;
    • 优点就是在制定字符集时不用考虑存储的问题,可以任意排布字符。

5.3 Unicode 到底使用哪种编码方案?

\quad Unicode是一个独立的字符集,它并不是和编码绑定的,你可以采用第一种方案,为每个字符分配固定长度的内存,也可以采用第二种方案,为每个字符分配尽量少的内存。

\quad 需要注意的是,Unicode只是一个字符集,在制定的时候并没有考虑编码的问题,所以采用第二种方案时,就不能从字符集本身下手了,只能从字符编号下手,这样在存储和读取时都要进行适当的转换。

  • Unicode 可以使用的编码有三种,分别是:
    • UTF-8:一种变长的编码方案,使用1 ~ 4个字节来存储;
    • UTF-32:一种固定长度的编码方案,不管字符编号大小,始终使用4个字节来存储;
    • UTF-16:介于UTF-8UTF-32之间,使用2个或者4个字节来存储,长度既固定又可变。

\quad UTFUnicode Transformation Format的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。

5.3.1 UTF-8

\quad 随着互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8Unicode的实现方式之一。

\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码在0x100000x10FFFF之间的字符,使用4个字节存储,4个字节分成前后两部分每个部分各两个字节,其中,前面两个字节的前6位二进制固定为110110,后面两个字节的前6位二进制固定为110111,其余部分使用字符对应的Unicode码减去0x10000的结果填充。
  • 大于0x10FFFFUnicode码无法使用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 所以,对于多字节的编码单元,需要有一个标记显式的告诉计算机,按照什么样的顺序解析字符,也就是字节序,字节序分为大端字节序(BEBig-Endian)和小端字节序(LELittle-Endian)。

  • 大端字节序表示高位字节在前低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端。这是人类读写数值的方法
  • 小端字节序表示低位字节在前高位字节在后,高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端。小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。

\quad 下面以“”字的Unicode0x4E2D为例来说明大端和小端:
在这里插入图片描述

5.5 BOM

\quad BOMByte Order Mark的缩写,是“字节序标记”的意思, 它常被用来标识文件是以UTF-8UTF-16UTF-32编码的。

\quad UCSUniversal Character Set)编码,即Unicode编码,中有一个叫做 “Zero Width No-Break Space”,中文译名作 “零宽无间断间隔”的字符,它的编码是FEFF。而FEFFUCS中是不存在的字符(只有编码没有实际的符号),所以不应该出现在实际传输中。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编码了。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软耳朵DONG

觉得文章不错就鼓励一下作者吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值