字符编码的发展简介
1.ASCII的由来[1]#
1.1 电报和摩斯代码#
用电子信息来表示书面文字的历史并不长。1837年,摩斯发明电报可以视为现代信息技术的开端。在手机流行后很长一段时间,电报并没有消失。电报的通讯是通过电线发送输入的通讯内容,在过去它通常用于发送大容量的文本,特别必须要以书面形式的情况下(比如小说)。在进行像订单、发票这样一些不是很重要的日常通讯,或者急需省下买带宽的钱的时候,也会选择电报通讯。如今的电子邮件和EDI或多或少是电报的逻辑后代。
最早的摩斯电报基于数字进行通讯,而不是像今天这样基于字母进行通讯。在电报通讯两端的操作者都有一本记录每个单词的唯一数字代码的字典。发送者根据字典找到单词对应的数字代码,并进行输入发送;接收者接受到数字后,查找字典找到数字代码对应的单词(这个过程可能是自动的,通过机械装置自动自动指向某个单词)。
1844年,摩斯的论证“WHAT HATH GOD WROUGHT”改变了这种方式。从此之后,电报开始发送我们熟知的”摩斯代码“,它实际上是摩斯的助理 Alfred Vail发明的。“摩斯代码”的数字化并不是指转化为1和0的流,而是基于电流的“开”和“关”两种状态。这是一项伟大的发明,在这之前电报基于电线发送不同的电压,仪表上的指针会发生相应的偏转。摩斯的发明容错性高,设备分辨“开”和“关”两种状态比分辨1/2和3/4要简单,这也是现代电脑基于二进制的原因。
摩斯代码并不是基于连续的”开“和”关“,而是基于”开“的时长,不同的”开“之间会被隔开。长”开“用”–“表示,短”开“用” . “表示。每个字母用1到4个” – “和” . “表示。常用字母的表示如下,I(. .)、A(. –)、N (– .). 和M (– –)。通常使用频率高的字母,用更少的” . “或” – “表示。数字用包含5个信号的序列表示,标点符号用包含6个信号的序列表示。”开“、”关“信号之间、字母之间和单词之间都会用空格分割,空格的长度依次增加的。
摩斯代码最早可追溯到1838年,在1844年首次使用,也被称为American Morse code。如图1,1848年,Friedrich Gerke对American Morse Code的代码点进行了更改,取消了不同长度的” – “和不同长度的信号间隔,只剩下” – “和” . “两种信号。1865年,The International Morse code的代码点大部分采用了Greke1848年设计的。o和p的代码点从Steinheil的代码系统取用的。赋予了j新的代码点,因为Greke没有对i和j的代码点做区分。x、y和z的代码点也做了修改。最终,The International Morse code只有e、h、k和n的代码点和原始的摩斯代码相同,如果忽略k、n中” – ”的长度调整的话[2]。
图1 从American Morse code到The International Morse code的变化
有趣的是,电报的信号可以操作螺线管使用触控笔在移动的纸带上挖出凹槽。就像我们经常在二战电影中看到的,通过收音机听哔哔声来解释摩斯代码的过程后来出现了(建议到参考资料[2]所引用的页面中去听一下);尽管有经验的电报人员可以通过听触控笔的点击来解释信号。这会发生在使用punched-tape systems的电报设备和早期的计算机上[2]。
1.2 电传打字机和Baudot code#
纸上的凹槽并不是我们想要的,我们希望在之上看到字母。但摩斯电报是基于“ – ”和“ . ”信号的,将“ – ”和“ . ”信号转换为纸上的字母是一个难题。1874年, Emile Baudot提出的 “电报印刷”是解决这个问题的第一步。
Baudot发明的设备并没有使用现在的打字机键盘,而是使用了5键的键盘(与图2类似),每个键有独立的电路连接。电报人员左手操作2个键,右手操作3个键,同一时间5个键的操作组合会发送一个字符。5个键共有32个代码,这不能包含所有的字母和数字。解决方案是使用两套字符集合,通过其中的2个代码来实现字符集合的切换,剩下的每个代码都可以对应两个不同的字符。该设备有两个字符库,包含28个字母(26个英文字母,2个法文字母)的LTRS库和包含28个数字、标点符号以及其它符号的FIGS 库。左手的2个键的3个组合没有对应的字符,2个键同时按下表示在LTRS库和FIGS库之间来回切换,按下其中1个键表示删除最后一个字符。
图2 与Baudot钢琴式键盘类似的键盘
当你要发送“I have 23 children.”这句话时,在设备上应该发送“I HAVE [FIGS] 23 [LTRS] CHILDREN [FIGS] .”。默认是LTRS模式,当你要发送数字或标点符号时,切换到FIGS模式;当你要发送字母时,再切换回LTRS模式。当然模式间的切换也可以通过添加第6个键来实现,但这在当时被认为会使设备变得很复杂。
尽管Baudot代码可以认为是5-bit的数字,但与ASCII编码的字符代码完全不同。因为Baudot代码在设计时,倾向于将高频的字母使用更少的键,这可以减轻手指和设备的疲劳,而ASCII编码不用考虑这个问题。
1899-1901年,Donald Murray发明了新的设备,它使用了打字机键盘(如图3 [4]),而不是Baudot的钢琴式键盘。Murray代码延用了Murray代码由five-bit组成和分为可来回切换的两个字符库的特性。但Murray为字符分配了新的代码,因为不用再向Baudot那样考虑手指疲劳的问题。Murray代码引入许多新的特性。比如,首次引入了“format effectors” 和 “control characters”,也被称为CR和LF代码,分别表示打字机回到行首和将压板前进一行。NULL或BLANK代码和DEL代码也被重新赋予了,一直到Unicode之前一直没有变过。NULL的代码是all bits off,DEL的代码是all bits on。在实操时,通常需要在耗时比较长的操作上使用NULL进行垫码。比如你发送了CR,打字机返回到行首需要一段较长的时间,这段时间内任何具体的字符都会被忽略,所以通常会在CR后发送大量的NULL。这一方面不会使具体的字符被忽略;另一方面当打字机返回到行首,但具体的字符还在传输中,并不会有任何的影响。除此之外,DEL字符也会被接收端所忽略。
Murray代码构建了其后50年各种电报代码的基础。Western Union在19世纪50年代一直使用基于Murray代码做了少量修改的代码。 一个欧洲的标准组织CCITT (Consultative Committee for International Telephone and Telegraph) 基于Western Union代码做了少量修改后,将其作为国际标准,它被称为ITA2(International Telegraphy Alphabet #2)。
图3 带有FIGS和LTRS键的电子打印机键盘[4]
今天我们说的 "Baudot代码"通常指ITA2代码,尽管ITA2代码与Baudot代码差别很大。相比于Baudot代码,ITA2代码新增了很多的“control codes”,比如space字符、BEL信号、WRU代码等[1]。在ITA2代码及其变体在19世纪30年代被提出后,作为有国际标准支持的编码方式,被很多的电子设备使用。ITA2代码的使用一直延续到19世纪60年代。之后随着电脑的出现,电脑通过打字机等终端设备与外界交流,交流使用的是当时的电传打字机代码。电脑处理字母数字等数据的另一种方式是使用punched cards,它有自己的编码机制[1]。
1.3 FIELDATA and ASCII#
介绍到现在,电传打字机的代码都是5-bit的代码,且包含两个字符库,可以通过控制键在两个字符库之间切换。字符代码的编译要取决于最近一次切换后所在的字符库。当你处理完全串行的字符流的时候,将无法随机读取并编译字符流中间的字符,因为不知道其所在的字符库。在计算机内存中,如果要读取并编译字符流中间的字符,需要从该字符开始向前搜索控制字符(LTRS或FIGS)的位置。所以字符代码后来添加了额外的bit来存储一个状态值(Punched-card代码也一样),这个状态值表示当前所在字符库(LTRS或FIGS)。后来很多的电脑都是基于6-bit的字符代码的,比如Kernighan and Ritchie曾说Honeywell 6000的int和short类型的数据是36bit的。
在19世纪50年代后期,电脑和电子通讯的企业在使用6-bit的电传打字机代码和punched-card代码的同时,人们也在开始指定新的标准,最后提出了ASCII代码标准。ASCII标准的前身是FIELDATA。1957年美国军队设计和使用的很多的通讯设备都使用了FIELDATA代码;当时民用的UNIVAC电脑基于FIELDAT代码的变体。
FIELDATA代码是7-bit的,在当时它被分为4-bit代码位和3-bit的控制位。当然,把它理解成ITA2的5-bit代码加2-bit控制位是更简单的。其中1个bit是“tag"位,表示所属的字符库,这类似于6-bit中的表示所属字符库LTRS/FIGS的bit。另一个bit表示所属的子字符库,属于字母库的大写字母库还是小写字母库; 属于supervisory bank 的supervisory bank或数字符号库。本质上,额外的bit第一次实现了大写字母和小写字母的区分,也使得包含控制代码、supervisory、通讯协议代码和变长字符代码的组成单元成为可能。
在FIELDATA开始使用的同时,ANSI的X3.4会议将 ANSI X3.4-1963作为美国信息交流标准代码。ANSI X3.4-1963是ASCII(发音“ASS-key”)的首版。X3.4会议的代表来自AT&T, IBM以及其它电脑和通讯公司,滑稽的是,IBM直到1981年IBM PC的问世才开始使用ASCII代码。ANSI X3.4-1963基本与FIELDATA一致,只做了一些改动。ASSCII-1963代码不包含小写字母,并将少数控制字符代码放在打印字符代码的范围内。这在ASCII-1967中得到修复,ASCII-1967与我们今天说的ASCII很接近了。ASCII-1967将控制字符代码的含义标准化了,将所有的控制代码(除了“DEL”)移到了控制代码的区域,并且添加了小写字母以及一些特殊编程语言符号。ASCII-1967也对ASCII-1963中一些代码的含义重新定义了,比如上箭头的含义由求幂修改为插入符号(兼抑仰音符号),左箭头的含义由赋值修改为下划线。
1.4 小结#
从发明电报到提出ASCII-1967的时间线总结如图4。

图4 从Morse Code到ASCII编码的发展历程
2.单字节编码系统[1]#
ASCII-1967被ECMA(European computer Manufacturers’ Association )采用作为国际标准ECMA-6。ECMA-6在1965年就提出了,在1972被ISO(International Organization for Standardization)采用作为ISO 646标准。ISO是各国标准机构的总领,它会汇集各国的标准加以修改,以适合各国人员使用,并最终作为国际标准发布。
ISO 646是基于ASCII发展而来的。相比与ASCII,ISO 646代码做了两方面调整,图5为ASCII代码,调整的代码点分别用灰色和黄色背景色标出。灰色背景的代码点表示在ISO 646中,这些代码点(共11个)的字符会被替换,在ISO 646会对应12个代码位置。这12个代码位置是开放的,被称为“national use”代码位置,不同国家的标准会赋予这些代码位置不同的字符。黄色背景的代码点是重音代码(accent mark),将重音代码前加上字母的代码和backspace代码,可以表示重音字母。其中,“ ` ”表示acute accent,” ' “表示grave accent,“ " ”表示diaeresis ,” ^ “表示circumflex accent,” , “表示tilde,” ~ “表示cedilla[3]。

图5 ASCII所有代码点及对应的字符,图中在ISO 646中分配不同字符的代码点用灰色和黄色背景标出了
2.1 ISO-2022#
ISO-2022,由ECMA-35发展而来,它为不同的字符编码方式如何分配代码提供标准。符合ISO-2022标准的字符编码方式可以包含C0、C1两个控制字符集合,以及G0、G1、G2、G3四个图形字符集合。ISO-2022的代码空间可以是7-bit或8-bit。
在7-bit的代码空间下,C0、C1集合表示0x00~0x1F范围内的值;G0、G1、G2、G3集合表示 0x20~0xFF范围内的值。在0x20-0xFF范围内,默认是G0集合,可以通过escape sequences(以esc字符开头的字符序列)切换到其他集合。在8-bit的代码空间下,C0集合表示0x00~0x1F范围内的值,C1集合表示 0x80至0x9F范围内的值;G0集合表示 0x20~0x7F(“GR area”)范围内的值,G1、G2、G3表示0xA0 ~0xFF(“GL area”)范围内的值。
Control functions(只发送信号,不输出字符)可以用一个控制字符或一个控制字符为首的字符序列表示。通常以ESC字符为首的字符序列被成为escape sequences。ISO-2022在7-bit系统中escape sequence被用于表示C1集合内的控制字符;或者在7-bit或8-bit系统中被用于在GR和GL区域间切换。
ISO-2022标准没有为大多数的代码赋予具体的字符,ESC、DEL和space键除外。字符可能取自其他标准,可以通过escape sequence在不同的标准间切换。
基于ISO-2022标准的标准可以赋予不同的编码区域任意的字符,并可以选择是否启用escape sequences用与切换编码标准或编码区域。大多数基于ISO-2022的标准都将ISO646 IRV(US ASCII)的图形字符放在G0区域;C0和C1区域以及C0 control functions都来自与ISO6429,其中C0区域的控制字符与ASCII相同。
2.2 ISO-8859#
ISO-8859系列是遵守ISO-2022标准的最重要的编码机制。ISO-8859标准包含14种编码标准,每个标准会包含不同于其它标准的语言下的字符集。每个标准都是一个独立的标准,比如ISO 8859-1,它包含无处不在的拉丁字符集。 ISO 8859系列的诞生缘于1982年ANSI和ECMA的联合项目。ISO 8859-1源于1985年的发布的ECMA-94。后来ECMA-94又添加了字符集,ISO 8859的前4部分都源于ECMA-94。ISO 8859的其它部分源于ECMA的其它标准。
ISO-8859的每个部分间既有相同点,又有不同点。相同点是它们都将ISO-646的图形字符分配在了G0区域,将ISO-6429的C0和C1控制字符分配在了C0和C1区域。也就是说,每个部分都包含基础的拉丁字母,向下兼容ASCII(ASCII是7-bit的,当以8-bit表示时,符合每一个ISO-8859标准)。它们的不同之处是G1区域的分配,它们都没有为G2和G3区域分配字符,也没有启用escape sequences;当启用escape sequences时,可以在G0和G1间切换,将G1区域的字符分配G0区域。
ISO-8859的各个部分不同点可以总结如下[1]:
ISO 8859-1 Latin-1 Western European languages (French, German, Spanish, Italian, the Scandinavian languages etc.)
ISO 8859-2 Latin-2 Eastern European languages (Czech, Hungarian, Polish, Romanian, etc.)
ISO 8859-3 Latin-3 Southern European languages (Maltese and Turkish, plus Esperanto)
ISO 8859-4 Latin-4 Northern European languages (Latvian, Lithuanian, Estonian,Greenlandic, and Sami)
这里只列出了ISO-8859前4部分。ISO-8859系列在如何分配8-bit的编码空间上提供了示范。ISO-8859的不同部分间的拉丁字母集合是大量重叠的,不同的主要是一些非拉丁字母,这些字母通常来自于其它系统的字符集。但是IS0-8859比之前的杂烩都更规范。
2.3 其他单字节编码系统#
有很多的8-bit编码标准,其中包括大量的供应商开发的标准。一些供应商开发的标准,被IBM和Microsoft称为”code pages“,它们都早于ISO-8859,与ISO-8859系列有相同的字符集,但字符顺序不同。也有一些code pages增加了非字母的字符,比如DOS box-drawing字符;这些code pages包含早于ISO-8859的早期的Mac和DOS 使用的code pages和一些扩展了ISO-8859的code pages。这些code pages大都向下兼容ASCII,但未必符合ISO-2022标准。比如,欧洲Windows系统的编码方式Windows code page 1252,它是ISO8859-1的超集,它包含了Latin-1字符集,但是为C1区域分配了图形字符(这种情况甚至会发生在C0区域。早期的ICM PCcode pages不仅在C1/G1区域分配了box-drawing和what-not up字符;而且为C0区域分配了图形字符,这很容易与控制字符混淆)。IBM也有一些源于EBCDIC的code pages,它们为ISO-8859系列的不同语言提供了支持。
在ISO-8859系列之外,也有一些8-bit的编码机制。这包含泰国的TIS 620,它符合ISO-2022标准,将泰语字母分配在G1区域;以及印度的ISCII (“Indian Script Code for Information Interchange”)标准,它将印度语的字符分配在G1区域(印度的不同书写系统之间很接近,相同代码点的字符可以理解为只是字号不同)。VISCII(“Vietnamese Standard Code for Information Interchange”) 和TCVN 5712(一个越南国家标准)标准都是源于ASCII,但是将越语字母分配在了C0和C1区域。TCVN 5172有一个版本在剔除了大量的字母之后,符合ISO-2022标准。
日本的JIS X 0201("JIS-Roman")标准也是符合ISO 2022标准的,他将 Japanese Katakana syllabary(日本Katakana音节表)分配在了G1区域。G0区域的一些”national use“的代码点被赋予了特殊字符。比如,将"¥"符号分配在了"/"的位置,DOS路径名在日本电脑将显示为"¥"。
3.字符编码术语[1]#
我们先介绍一下字符编码中的术语,这有助于接下来介绍更多的编码方式,也将刚刚介绍的内容中模糊的术语具体化。
从图形字符到计算机内存或其它存储设备中的bit,不是一步完成的,而是分几步完成的。IAB(The Internet Architecture Board)提出了3级的编码模型。Unicode标准(Unicode Technical Report #17)提出了5级的编码模型,Unicode标准将IAB的一个隐藏的级别拎了出来,并插入了一个新的层级。我们这里讨论UTR #17,这会使介绍Unicode变得简单。
编码模型的第一层是abstract character repertoire。可以理解为要编码的字符集合,比如”拉丁字母“或”英语、西班牙语、法语、德语等字母“定义的字符集合。对于很多西方语言,这个步骤是简单直接的。但对于东方亚洲语言,这会变得复杂。当你要定义abstract character repertoire的时候,你要考虑是什么组成了”字符“?这里,”字符“可以简单的理解为”一个被编码的独立语义单元“。一个编码标准需要界定字符的含义。比如说,”重音字母“(字母+backspace+重音标识,详见第2节)是否可以认为是独立的字符,或者说重音标识本身作为一个独立的字符,与字母和backspace字符组合成重音字母?还有,在不同上下文中一个字符的不同形状是否都可认为是一个字符,或者说这些形状的底层的不变的语义单元是否被认为是一个字符?Unicode将”字符“定义为一个独立的语义单元,明确避开字形变体和表示形式,并明确表示将重音标记和其它组合标记视为独立的”字符“。Unicode外其它的标准对字符的定义可能不同。后文中使用的都是Unicode中定义的”字符“,要与”字形“区分开来。
第二层是the coded character set,记录了abstract repertoire中的字符和坐标之间的映射关系。大多被编码的字符集将字符映射到一个表格的x和y坐标,尽管这些坐标可以被映射到单一数字的集合上。与编码字符集相对应的是编码空间的概念,编码空间包含所有字符的表维度。编码空间可以用一组数字(16u16编码空间)或者单一数字(256-字符编码空间”)表示。通常编码空间受到表示一个字符的存储单元大小的限制(不是绝对的),它被用与简洁地表示编码空间(8-bit编码空间)。有时编码空间会拆分为多个子集,这些子集被命名为”行”、“平面”(plane)或“列”。编码空间的一个位置被成为code piont。被字符占据的一个位置被称为code point value。 一个coded character set是指字符到code point value的映射。
第三层是the character encoding form,也被称为“存储格式”。它是指抽象的code piont value到整数序列code unit的映射。对与固定长度的编码,这通常是一个空映射,但也不绝对。对于变长的编码,映射变得复杂,会出现多个code point映射到一个code unit,或者一个code point映射到多个code unit的情况。
第四层是the character encoding scheme,也被称为“序列化的格式”。它是指unit value到实际bit序列的映射。映射时要处理好基本的两点。一是大于一个字节的code unit会被映射到一个字节序列上,在通讯和存储的编码文本中,字节序列中的哪个字节在前是需要指定的(对于Unicode编码,通过Byte Order Mark指定[5])。二是一个字符编码机制可能有两种及两种以上的字符编码规则;为了使unit value映射到不同的数字范围,可以通过在序列化的数据流中写入额外的字节,以实现字符编码规则的切换,或者对不同编码规则下对code units做额外的转换。
字符编码标准通常定义了上述4层的转变:从characters到code points,从code points到code units,从code units到bytes;尽管一些转变是隐式的。但有时你会看到字符编码标准对不同层级的单独定义,比如说,亚洲编码标准通常会这样。
第五层是transfer encoding syntax,它通常被单独定义,或多或少与其它四层是正交的。正交是指第五层级的映射与其它四层的映射都是一一映射的。这是编码机制定义的对bytes的额外处理。通常的处理有两种。一种是bytes对应的整数值映射到为适用于某些环境而更受限的值。另一种是将byte序列映射到更短的byte序列。
下面我们将这些术语应用到已经介绍过的编码标准上。ITA2是一种电报编码,它将abstract character repertoire拆分为FIGS和LTRS两个字符库,每个字符库将repertoire中一半的字符映射到5-bit编码空间,这个5-bit编码空间通常是一个2行16格的表格。The character encoding forms以直接的方式将坐标映射到5-bit的code units上。The character encoding schemes 也以直接的方式将code units映射到bytes。除了字符映射的字节,最终生成的字节里还包含用于切换FIGS和LTRS字符库的字节。如果你考虑将 The character encoding schemes映射到物理介质上,还需要考虑字节中最重要的bit在纸带最左边的孔还是最右边的孔。
ASCII是很简单的,它将 character repertoire映射到7-bit共128字符的编码空间,通常表示为8u16的表格。从code point到code unit和从code units到bytes的映射都是明显直接的。如果编码空间为8-bit的字节的话,the character ecoding scheme除了将code units映射到7-bit外,还包含将7-bit字节填充到8-bit字节。
ISO2022提供了256字符的编码空间的基本布局的标准,基本布局分为C0、C1两个控制字符集合,以及G0、G1、G2、G3四个图形字符集合。但ISO2022并没有定义任何的具体的字符,也没有具体说明如何在G1和G2间切换,以及将具体的字符集合映射到G1和G2区域。
4.多字节编码系统[1]#
对于使用中国字符的语言,像汉语、日语和韩语,字符编码不同层级之间的区别更有趣。没人知道中文字符到底有多少,很可能超过100000个。大多数说汉语的人有5000个常用工作书写汉字;说日语和韩语的人通常会借助辅助书写系统来使用汉字,他们的常用工作书写汉字比说汉语的人要多。
4.1 东亚编码字符集#
当面对这么多要处理的字符时,你会定义一些你感兴趣的汉字集。比如日本政府颁布了Gakushu Kanji,它包含小学要学的1006个汉字;还颁布了the Joyo Kanji,它包含中学要学的在政府文件和报纸上常用的1945个汉字;以及 Jinmei-yo Kanji,它包含285个被批准用于个人姓名的汉字。其它使用汉语的国家会颁布类似的字符集。这些字符集只是标准化了 abstract character repertoire,并没有尝试为字符分配数字。
The Japanese Industrial Standards Commission (JISC,日本的ANSI)带头制定汉字的字符集标准,使汉字可以在计算机上展示(日本的电报代码出现的更早,在这里我们不谈论)。第一个标准是发布于1976年的IS X0201。它是源于ISO 2022的标准,将ISO 646日语版的字符分配在G0区域,将Japanese Katakana syllabary分配在G1区域。它没有编码汉字。它是8-bit的编码,后来变得非常重要。
第一个编码汉字的日语标准是JIS C 6226,它发布于1978年,后来改名为JIS X 0208(该标准相关工作最早可追溯到1969年)。JIS X 0208使用了介绍到目前为止最大的编码空间,94 u 94的表格。数字94不是巧合,它是ISO2022中G0区域可分配的代码点个数。它提供了符合ISO 2022的7-bit编码机制。每个字符可以用由一对7-bit的代码组成的code units表示(实际上,是将一个以94u94的坐标表示的14-bit的code point,映射到包含2个7-bit代码的code units)。
JIS X 0208不仅包含6355个汉字,这些汉字可以按使用频率和重要性分为2级(level 1 和level 2);还包含拉丁语、希腊语和Cyrillic字母, the Japanese Hiragana and Katakana syllabaries,以及各种各样的符号(symbol)和标点符号(punctuation mark)。
发布于1990年的JIS X 0212是JIS X 0208的扩展,它增加了5801个汉字,以及一些符号和在拉丁语和希腊语字母表中的补充字符。它延续JIS X 0208使用94 u 94的编码空间。发布于2000年的JIS X 0213增加了另外的5000个汉字(level 3和level 4,我不知道JIS X 0212中新增的汉字是哪个level),编码空间延续了之前的标准。
其它使用汉字的国家在设计字符集标准时使用了JIS的模型。这包括发布于1987年的中国的GB 2312,发布于1992年的台湾的CNS 11643,以及发布于1987年的南韩的KS X 1001。所有这些标准都是用94u94的编码空间,但是每个标准中的汉字和母语字符对应的code piont差别很大。
相比于刚提到的这些标准,CNS 11643编码的字符数最多。它包含16个plane(实际上有7个plane被使用),共编码48027个字符。因为它包含16个plane,它可以在同一个编码空间上分配不同的字符集合。
将这些编码空间为94u94的字符集的代码点的行和列的值加上32,以对应于ISO 2022的G0区域,将得到简单的ISO 2022派生的字符编码形式。基于此有两种encoding scheme,一种是按照先行数后列数的顺序进行编码,另一种是按照先列数后行数的顺序进行编码。
4.2 东亚编码字符集的字符编码机制#
从这里开始事情变得有趣。不能仅仅将东亚字符标准视为2个字节,不同的打包机制已经被开发出来,以节省空间,或者将两个或多个不同的编码字符集组合起来。主要的字符编码机制(character encoding scheme)可分为以下4种。
我们最熟悉的是Shift-JIS。它将JIS X 0201和JIS X 0208编码字符集结合起来。JIS X 0201的代码点(code point)的值被映射到单一字节。JIS X 0201中未使用的字节值(0x80-0x9F和0xE0-0xEF)被用于表示JIS X0208中字符映射后的字节序列的第一个字节,字节序列的第二个字节值的范围为0x40–0x7E或0x80–0xFC。JIS X 0208中行列的值不是直接映射到2个字节的序列的。事实上,Shift-JIS还添加了用户可自定义的字符空间,它被表示为2个字节的序列,第一个字节的值的范围为0xF0–0xFC。
有趣的一点是Shift-JIS包含所有的来自JIS X 0201和JIS X 0208的字符。它们有不同的含义,来自JIS X 0201中的字符被认为是半角的(占据一个典型的汉字字符的一半空间),来自JIS X 0208中的字符被认为是全角的(占据一个典型的汉字字符的全部空间)。
我们第二熟悉的编码机制是EUX(Extended UNIX Code),最早被开发用于UNIX系统,在1991年被UNIX的供应商标准化。它基于ISO 2022的结构,字节长度为1–4个。EUC定义了4个独立的代码集合(code set)。code set 0总是ASCII(或ISO646的变体),它映射到的字节与ASCII中的C0和G0区域相同。code sets 1、2和3都使用G1区域的值表示。code set 1中字符的表示是不用修饰的。code set 2中字符以C1中的控制字符SS2(0x8E)开头。code set 3中的字符以一个不同的C1中的控制字符SS3或0x8E开头。set 0的字符被表示为1个字节,set 1中的字节用1-3个字节表示;set 2,3的字符用2–4个字节表示(应为要以SS2或SS3开头)。
EUC的code set 1–3中的字符是有区域差异的,不同区域有自己的EUC变体。下面列出了EUC-JP等4种EUC的变体。
EUC-JP (Japan): Code set 0: JIS Roman (i.e., the Japanese variant of ISO 646) [onebyte]
Code set 1: JIS X 0208, with the row and column numbers shifted up into the G1 range [two bytes]
Code set 2: Katakana from JIS X 0201 [SS2 plus one byte]
Code set 3: JIS X 0212, with the row and column numbers shifted up into the G1 range [SS3 plus two bytes]
EUC-CN (China): Code set 0: Chinese ISO 646 [one byte]
Code set 1: GB 2312, with the row and column numbers shifted up to the G1 range [two bytes]
Code sets 2 and 3: Not used
EUC-TW(Taiwan):
Code set 0: Taiwanese ISO 646 [one byte]
Code set 1: CNS 11643, plane 1, with the row and column numbers shifted up into the G1 range [two bytes]
Code set 2: All of CNS 11643, the row and column numbers as above, preceded by the plane number, also shifted into the G1 range [SS2 plus three bytes]
Code set 3: Not used.
EUC-KR (Korea): Code set 0: Korean ISO 646 [one byte]
Code set 1: KS X 1001, with the row and column numbers shifted up into the G1 range [two bytes]
Code sets 2 and 3: Not used.
接下来是ISO 2022 7-bit编码系列。它基于ISO 2022的7-bit的code points来表示字符。同样地,因为地域的差异,有不同的编码标准。基本思想是编码系统是多模式的,你可以使用ASCII的SI和SO(shift in和shift out,0x0F和0x0E)在单字节模式和双字节模式间切换。你也可以使用ISO 2022中的SS2和SS3代码(这里是指escape sequences的化身,因为是7-bit的)引用不同的双字节字符集合里的字符。ISO 2022的各种国家变体使用不同的escape equences(通常是4个字节)以进行双字节模式下的不同字符集合间切换。不同的变体对不同的字符集合进行编码,下面列出了几种变体。
ISO-2022-JP JIS-Roman and JIS X 0208
ISO-2022-JP-1 and ISO-2022-JP-2 ISO-2022-JP plus JIS X 0212
ISO-2022-CN ASCII, GB 2312, CNS 11643 planes 1 and 2
ISO-2022-CN-EXT ISO-2022-CN plus the rest of CNS 11643
ISO-2022-KR ASCII and KS X 1001
从一个变体到另一个变体的差异很大,并且它们并不严格遵守ISO 2022标准。更多细节请参考Ken Lunde’s CJKV Information Processing。
最后,另一种7-bit的编码标准HZ,它使用打印字符序列进行单字节模式(ASCII)和双字节模式(GB2312)的切换。序列"{"表示切换到双字节模式,序列"}"表示切换到单字节模式。
4.3 其它的东亚编码系统#
最后,在刚介绍的4种character encoding scheme之外,再介绍几种编码标准。
台湾的Big5标准是一个工业标准(命名的由来是它得到了5个大的电脑商的支持)。Big5早于CNS 11463,具体可追溯到1984年,是台湾的官方标准(CNS 11463向下兼容Big5,可以理解成Big5的扩展和改进版本)。Big5使用94u157的编码空间,可以容纳14758个字符。他是符合ISO 2022标准的变长的编码标准。ISO646的台湾版本中的字符被C0和G0区域的单字节序列表示。中文字符以及其它常用的字符用双字节表示,第一个字节在G1区域,第二字节在G1或G0区域(G0区域中的部分值)。
CCCII (Chinese Character Code for Information Interchange)是另一个台湾的工业标准,它早于Big5在1980年发布,对CNS 11643产生影响。它是一种定长的编码方式,将每个code point映射到3个7-bit的code unit上。它有94u94u94的编码空间:16 layer,每个layer有6个94u94的plane。code point的第一个字节是layer和plane的数值,第二个字节是行的值,第三个字节是列的值。
Johab是KS X 1001标准的一个可选编码机制。KS X 1001编码了2350个韩语音节,但它提供一个可选的方法,这个方法可以编码11172个韩语音节(韩语音节是一个字母系统,但这些字母通常被分组为字节块,这些字节块在韩文系统中以类似汉字的方式被编码为code units)。Johab利用了韩文字母的特性:不只是给每个音节分配一个数字,这些音节被分解为组成字母或jamo(一个音节中包含0-3个),每个字母或jamo被编码为5个bit。你得到了15个bit的字符代码,它可以被编码为2个byte。ISO 646的韩国变体中的字母被分配在G0区域,双字节字符序列的第一个字节侵入了C1区域,第二个字节被分配在G0、C1或G1区域。因此,johab编码方式不遵守ISO 2022标准。
5. 编码方式的统一[1]#
5.1 ISO 10646和Unicode#
5.1.1 同一编码的引出#
介绍到现在,我们手上的编码标准一团糟。总共有几百种不同的编码标准,许多标准是多余的,它们对同一字符进行不同的编码。即使在ISO 8859系列中,有10种不同的方式编码拉丁字母,每一种方式编码的拉丁字母集合稍有差异。
你在输入字符的时候必须考虑到正在使用的编码机制,传输或存储的字节内容中必须包含你正在使用的编码机制,不然计算机可能将字节解释成不是你想要的字符。比如你在美国电脑上打开一个日本网站,你看到的将是不知所云的内容而不是日文。7-bitASCII在绝大多数的编码机制中是通用的,但像ISO 646,修改了的”national use“字符编码将不再通用。或者你的文本在经过EBCDIC-based的系统后变得完全混乱。
在文本中混合使用多国语言时,必须借助辅助的数据结构来跟踪记录每一段文本的编码机制。你也可以使用像ISO2022中提及的或其它类似的编码切换机制,但这对用户或处理软件来说都是繁琐的。
现在面临了很多问题,它们值得被解决:
1)编译不同国家的语言有许多种不兼容的编码方法,但从编码内容本身无法得知其使用的编码标准。
2)有限的编码空间,每一个编码标准都不完整,编码标准之间也有很多编码冲突。
3)变长的encoding scheme编码后的code unit是相交的,这给界定字符的边界和计算字符数带来困难,也使得数据极易损坏,一个字节丢失可能会导致所有的字节都解释错误。
4)像ISO2022这种编码切换机制是很繁琐的,解释存储的文本变得很困难,在解释文本随机的字节时,必须向文本之前查找以确认该字节所使用的编码机制。
5.1.2 ISO10646和Unicode#
我们需要统一的编码机制来解决这些问题,为每个字符分配唯一的code point。1984年,国际的标准组织和后来发展为ISO10646的早期国际标准组织和电子会议制定了一个标准,就是后来ISO10646。ISO10646和ISO 646是有联系的,它是ISO 646的扩展。
与此同时,Unicode标准也被制定出来。Unicode起源于Joe Becker和Xerox的一篇论文,论文提出了国际编码标准的理想特征,并将其命名为“Unicode”。来自Xerox、Apple和一些其它公司的专家组成了非正式的组织,一起致力于Unicode标准的制定。这个组织后来发展为今天的Unicode联盟。
它们独立发展着,ISO10646的早期版本基于32-bit的code point,具有256u256u256u256的编码空间。不是所有的编码空间中的code point都是可用的,字节值0x00-0x1F和0x80-0x9F,对应于ISO2022的C0和C1区域,不允许在4个字节的code point的任一字节使用。所以ISO10646的编码空间实际上是192u192u192u192,被看作是192 “groups” of 192 “planes” of 192 “rows” of 192 “cells”。一个字符占4个字节不是很好,所以早期的ISO10646有多达5种编码形式,它包含具有1-4不同字节数的code unit,和变长的编码机制。变长的编码机制允许code points被多个单字节的code units表示。在较短的编码机制(比如UCS-2)中,使用了与ISO 2022相似的escape sequences的方式将code unit从一个编码空间切换到另一个编码空间。
较短的编码机制UCS-2,可以通过escape sequence在code point的不同plane间切换。如果所有的字符都来自同一plane,就可以在code unit中省去plane和group,只保留row和cell的值。“basic multilingual plane”(BMP)包含了除汉字外几乎所有当时书写系统的字符。中文繁体、中文简体、日文、韩文的中文被分配在不同的plane。也提供了一些特殊或私有用途的plane。
ISO10646的早期版本在第一次投票活动中失败了。并不是因为C0和C1区域被禁用,C0和C1禁用后不可用的code point是有限的。但是因为首位前添加0的ASCII编码不是合法ISO10646 的code point,无法用C语言中的wchat_t类型存储10646的code point,并保持反向兼容。使用escape sequence的方式在不同的plane间切换的方式不可能受欢迎,不同国家也不愿将书写系统绑定在一个plane上。
与此同时,Unicode联盟制定了16-bit的code piont的标准,这相当于ISO10646的一个plane,对code point中的字节值没有限制。Unicode没有多种编码形式或机制,没有变长的解释机制,只有Unicode本身。Unicode和ISO10646的一大区别是对中文字符的处理。尽管中国、日本、韩国、越南和台湾对中文字符的使用差别很大,但同时中文字符,且不同的中文字符集中字符的重叠率很高。对不同语言中的同一中文字符进行不同的编码是无意义的,这就像对英语、西班牙与、法语、意大利语和德语中的字母A进行不同的编码。
Xerox和Apple一直在进行中文字符集的设计,它基于中国内部的工作,比如说CCCII,试着统一不同国家的中文字符编码。Xerox/Apple的努力促成了Unicode的早期草稿,它被推荐给ISO以添加到ISO 10646中。Xerox/Apple和中国的共同努力促成了CJK Joint Research Group,它制定了ISO10646中中文字符集的第一个版本。这个组织现在被称为Ideographic Rapporteur Group(IRG),现在是WG2正式的小组委员会,仍然负责ISO10646和Unicode中的中文字符集部分。
Unicode的最初想法是将韩语音节、少用的汉字和少用的重音字母组合使用多个code point表示,以节省编码空间。多个code pont中的每个code piont都有独立于其它的预先定义好的含义。Unicode还有一个私有的编码区域,用于一些特殊字符的编码,比如说一些不常用的书写系统会使用的。Unicode之外的高级别协议会为私有的编码区域分配特殊的字符。
5.1.3 ISO10646和Unicode的演变#
在ISO 10646投票失败以后,10646和Unicode的小组开始谈论进行技术合并。10646保留了32-bit的分为group、plane、row、cell的编码空间设计,但是取消了code point中的字节禁用C0和C1区域的限制,相当于开放了所有的32-bit的code point(实际上是31-bit,最高位是禁止打开的,这使得10646的code point存储为有符号或无符号的数据均可,不用担心影响内容)。编码形式和机制也被简化了,类似于ISO 2022中的escape sequences以及1-byte、3-byte和变字节的编码方式都被移除了,仅留下了UCS-4和UCS-2。UCS-4从code point到字节的转变是直接的,UCS-2允许将Basic Multilingual Plane(BMP)用2个字节的code unit表示。
ISO 10646和Unicode最终都添加和移动了字符。Unicode放弃了将韩语音节和少用的中文字符用多个code point表示的想法;同时ISO 10646移动了统一的中文字符库,将其放在BMP范围内。1991年,从这点来看,具有各自字符库和code points的Unicode和ISO 10646开始变得同步。Unicode1.1,减去统一中文字符库的部分,是合并后的第一个版本。统一中文字符库在1993年完成,此时10646发布了第一个版本ISO/IEC 10646-1:1993。从那以后它们一直保持同步。
两个标准没有在合并中消失,Unicode和ISO 10646独立存在着,是不同的标准。保持两个标准的同步,需要付出巨大的努力。两个标准的管理委员会有很多重叠的成员,双方都很关心相互间的同步。从合并到现在,两个标准的字符库实际上保持了一致。
ISO 10646中plane的概念最终被Unicode采用,以适应字符库的快速增长。Unicode2.0添加了某种代理机制(就是现在的UTF-16),以表示BMP范围外的字符;使用闲置的code point组成一对,用于表示BMP范围外的字符。这使得Unicode2.0的编码空间扩展为相当于ISO 10646前17个plane的容量。WG2同意宣布Plane 16以上的代码空间是禁用的,它们的编码空间仍保持同步。
从Unicode2.0开始,Unicode的编码空间从一个256u256的plane,变为17个256u256的plane,对应于ISO10646的0-16plane。这意味这Unicode的code point现在是一个21-bit的值。Unicode和ISO 10646都定义了3种编码机制,UTF-32、UTF-16和UTF-8。UTF-32将21-bit的code point编码为一个32-bit的code unit;UTF-16将其编码为1个或2个16-bit的code unit;UTF-8将其编码为1到4个8-bit的code unit。它们进一步定义了7种编码机制,UTF-8、UTF-16的3种风格和UTF-32的3种风格;不同的风格被用于不同字节顺序的机器架构。
5.1.4 ISO10646目前的相同点和不同点#
Unicode和ISO10646定义了相同的编码字符集和字符编码机制。但它们内容上和更新发布的方式上有所不同,最重要的包括以下几点:
1.ISO10646定义了正式的方法用于声明实现支持哪些字符,Unicode将它留给了临时方法;
2.Unicode定义了关于每个字符的大量语义信息,并提供了一大堆实现指南和规则;ISO 10646仅仅给出了字符和分配的code point;
3.Unicode标准可以在任何书店获取到(书籍间的更新会发布在网络上),ISO 10646需要从ISO成员组织订购。
4.ISO 10646标准分为2个部分,ISO-10646-1和ISO-10646-2。ISO-10646-1定义了BMP范围内代码的结构和分配标准;ISO10646-2定义了其它16个plane的代码分配。Unicode仅发布一个统一的标准。
另一个重要的区别是标准的更新方式。Unicode有一个版本号机制,小的改动对应于版本号小数点后的数字,作为技术报告发布;大的改动对应于版本号小数点前的数字,将会发布一本新的书。ISO 10646的版本发布频率更低,但是修正和勘误要频繁的多。
这意味这一个标准可能周期性的领先另一个标准。10646在修正被接受后通常会领先一点,一堆修正聚在一起才会形成新的Unicode版本。即使这样,两者的某些特定版本之间也有直接的映射关系。映射关系如下:
Unicode 1.0 (pre-merger)
Unicode 1.0 (pre-merger)
Unicode 1.1 ISO 10646-1:1993
Unicode 2.0 ISO 10646-1:1993, plus amendments 1 thru 7
Unicode 2.1 ISO 10646-1:1993, plus amendments 1 thru 7 and 18
Unicode 3.0 ISO 10646-1:2000 (which was formed from ISO 10646-1:1993 and amendments 1thru 31)
Unicode 3.1 ISO 10646-1:2000 and ISO 10646-2:2001
5.1.5 总结#
在19世纪80年代,Unicode和ISO 10646先后提出之后,它们具有不同的character repertoire和code point,如图。它们的编码机制也不同,ISO 10646有UTF-1、UCS-2、UCS-4等编码机制;而Unicode没有不同的编码机制[1],只是Unicode,可能是direct mapping的。在1991年二者开始合并并开始变得同步,在1996年Uncode升级到Unicode2.0后,它们具有相同的character repertoire和code point。它们的编码机制主要有UTF-8、UTF-16、UTF-32、GB 18030等。其中,UTF-8是由UTF-1发展而来的;UTF-16是由UCS-2发展而来的,UTF-32是由UCS-4发展而来的。

图6 ISO 10646和Unicode合并前后的编码过程
5.2 Unicode标准的维护#
Unicode标准的维护和推进是在UTC(Unicode Technical Committee)上进行的。会议的整个过程是很琐碎的,不能掉以轻心。会议会对提案进行讨论,提案的采纳通过投票机制。ISO10646标准由ISO JTC1/SC2/WG2进行维护,许多WG2的成员也是UTC的成员。
Unicode会对外开放邮箱,UTC成员或其他个人都可以向邮箱发送建议,这些建议会被分成两个邮件列表,一个UTC邮件列表和一个所有人邮件列表。很多的提案来源于对两个邮件列表中的一个进行的信息讨论,在信息讨论并进行淘汰之后成为正式的提案。值得推荐的是,一个建议在被提交之前,先发送到邮件列表中,这可以减少建议被提交到UTC之后的来回。
任何人都可以向Unicode标准提建议。但在提交前,请先确认你已经完全理解了Unicode标准,并确保你的建议在现有的Unicode结构和限制下是有意义的。你也需要为你的建议准备充分的证据。一个建议被采纳的过程是漫长的,在过程中提案的推进是非常耗时的。
5.3 编码机制UTF-8与GBK#
5.3.1 UTF-8[6]#
UTF-8不是一种编码标准,而是一种基于Unicode的编码机制,他将Unicode的code point编码为1-4个8-bit的code units。UTF-8向后完全兼容ASCII,也就是说UTF-8可以在任意的支持8-bitASCII派生的编码方式的环境中使用,环境一直可以编译并展示这些7-bit的ASCII字符。如果这7-bit的ASCII最高位bit被设置,则UTF-8不能向后兼容,它在UTF-8表示的字符与传统的编码方式不同。
也就是说,从U+0000至U+007F的code point在UTF-8中以直接的方式映射到code unit。如图7,你只需要取出code point的后7位bit,并在最前面的bit位补0即可。

图7 UTF-8的1-byte编码机制
如图8,从U+0080至U+07FF的code point会映射到2个8-bit的code units。后6位bit,加上0x80,就是第二个字节;紧接着的5位bit,加上0xC0,就是第一个字节。

图8 UTF-8的2-byte编码机制
如图9,从U+0800至U+FFFF的code point映射到3个8-bit的code units。前4位bit,加上0xE0,为第一个字节;紧接着的6位bit,加上0x80,为第二个字节;最后的6位bit,加上0x80,为第三个字节。

图9 UTF-8的3-byte编码机制
最后,plane1至plane16的code point(非BMP范围的字符)映射到4个8-bit的code units,如图10。前3位bit,加上0xF0,为第一个字节; 依次接下来的每6个bit,加上0x80,为接下来的3个字节。

图10 UTF-8的4-byte编码机制
按照这样的方法,可以ISO 10646的23bit至31-bit范围的code point映射到5个或6个字节上。这在过去时有意义的,但现在WG2已经明确表示不会为plane 18及以上分配字符。
相比与UTF-16,UTF-8避免了很多问题,它将值在不同范围的字节分配在字符的不同位置上。
00–7F Single-byte character
80–BF Trailing byte
C0–DF Leading byte of two-byte character
E0–EF Leading byte of three-byte character
F0–F7 Leading byte of four-byte character
F8–FB Illegal (formerly leading byte of five-byte character)
FC–FD Illegal (formerly leading byte of six-byte character)
FE–FF Illegal
只有trailing byte的位置时模糊的。通过字节值可以判断这个字节是trailing byte,但无法判断这个字符有多少个字节,或者这个字节是字符的第几个字节。但是依据UTF-8的设计,可以判断你最多只需要向前或向后3个字节。
UTF-8也有一些缺点。暂不详述。
5.3.2 GB18030#
5.3.2.1 GB系列编码方式的发展[7]
GB2312-1980是中国的官方字符集,用于编码简体中文字符。在GBK和GB18030成为新的国家强制标准前,GB2312-1980是当时的国家强制标准。GB2312至今仍作为GBK的一个子字符集被广泛使用。截止到2021年11月,GB2312是web上最受欢迎的中文字符集,它用于中国的6.9%的页面和国际上0.1%的页面。GB 2312中的字符编码空间为94u94,每个字符的2-byte代码点用区位(kuten)表示,比如”外“位于第45 row和第66 position,它的区位代码为45-66。GB 2312-1980包含682个符号和6763个中文字符。
GB是Guojia Biaozhun的缩写。1993年,Unicode1.1标准发布,包括了中国大陆、中国台湾、日本和韩国的20902个字符。紧随其后,中国发布了相当于Unicode1.1的国标GB 13000.1-93。
GBK中的K是kuozhan的缩写。1993年,GBK字符集被定义为GB2312-80的扩展,同时它也包含GB13000.1-93中的字符,这些字符用GB2312-80中未使用的code point表示。因此,GBK是向后兼容GB2312的。这里的兼容是指使用GBK的解码器可以解码GB2312编码的内容,两种编码方式中同一字符编码后的bytes是相同的,code point可能不同。
Microsoft在Windows 95和Windows NT 3.51上将GBK实现为 Code Page 936。GBK从来不是官方的标准,但是在Window 95中的广泛使用,使其成为事实上的标准。GBK的存在弥补了GB2312-80和GB 13000.1-9之间的差距。
1995年,中国信息技术标准化技术委员会制定了《汉字内码扩展规范》。该规范就是GBK1.0,它轻微扩展了Codepage 936,添加了95个GB 13000.1-93中没有的字符,并临时为PUA(Private Use Area)分配了字符。
微软后来在Code Page 936中新增了欧元符号(€)的字符,将其分配在0x80代码点。这是GBK1.0所不包含的。
2000年,GB18030-2000标准发布,取代了GBK1.0并保持了与GBK1.0的兼容性。他通过实现4个字节的字符空间(126u10u126u10)提升了定义的汉字数量和可扩展的汉字数量。GB 18030的包含1个和2个字节的子集有时也被成为GBK。与Unicode的code point的映射也有轻微的变化,这是由于一些之前Unicode未定义的字符现在已经被Unicode定义了。
GB 18030-2005与GB 18030-2000的编码机制是相同的,唯一的区别是ḿ字符的GB到Unicode的映射[8]。如表1,在GB18030-2000中,字符A8 BC(ḿ) 被映射到Unicode的PUA(private use area)区域的代码点U+E7C7,字符81 35 F4 37(不对应任何图形字符)被映射到代码点U+1E3F,此时Unicode还没有为U+1E3F分配字符;后来Unicode将ḿ字符分配到代码点U+1E3F,GB18030-2005在保持ḿ字符的字节序列A8 BC不变的基础上(保持对GB18030-2000及之前的GB系列编码方式的直接兼容性),将A8 BC(ḿ)对Unicode的映射修改为U+1E3F(ḿ),而将原来(GB18030-2000)映射到U+1E3F的字节序列81 35 F4 37映射到U+E7C7(PUA区域的代码点)。与GBK1.0相比,GB18030中81个字符的字节编码的与Unicode编码的映射关系被修改,这些字符之前被分配在Unicode的PUA区域,后来这些字符被Unicode收录。GB 18030-2005中仍有24个字符分配在PUA区域。根据Ken Lunde[8],2018年新修订的GB18030的草案这些字符将不再分配在PUA区域。
表1 GB 18030-2000和GB 18030-2005中对代码点A8 BC(ḿ)的不同映射

GB系列编码的发展可以总结如表2。GB18030的1-bye和2-byte编码相当于GBK1.0编码加欧元符号€,因此GB18030是直接兼容GBK1.0的。W3C在2015发布的技术推荐中[10],将GBK编码器定义为GB18030编码器剔除4-byte序列后的部分,外加欧元符号€,将GBK解码器定义为GB18030解码器。也就是说,然后GB18030是直接兼容GBK的,GB18030解码器可以解码GBK的内容。
表2 GB系列编码的发展历程

GB 18030-2005标准中的一部分是国家强制软件必须实现的[9]。GB 18030-2005 的国家强制部分包括1-byte、2-byte和4-byte的CJK Unified Ideographs Extension A编码,这部分完全落在BMP的范围内。
5.3.2.2 GB18030与Unicode的映射[8]
GB18030定义了1-byte、2-bye和4-byte的编码。1-byte和2-byte的code point其实包含了GBK中的字符,外加欧元符号€、PUA区域和垂直标点符号。4-byte可以看成是2个2-bye的code unit。每个code unit的第一个字节值的范围为 0x81 to 0xFE,第二个字节的值的范围为0x30–0x39(ASCII中0-9的数字编码)。
表3 GB18030的字节序列与Unicode代码点的映射关系

不幸的是,GBK18030中4-byte的字节序列与Unicode的code point间的映射没有简单的规则。仅对于Unicode的没有映射过的code point,字符代码可以进行顺序分配,比如下面列出的几个Unicode的code point。
U+00DE (Þ) → 81 30 89 37
U+00DF (ß) → 81 30 89 38
U+00E0 (à) → A8 A4
U+00E1 (á) → A8 A2
U+00E2 (â) → 81 30 89 39
U+00E3 (ã) → 81 30 8A 30
WHATWG和W3C使用一个偏移表(offset table)来高效的翻译code point。ICU和glibc使用了相似的方式以节省大的顺序块所占用的空间。如果一一记录4-byte字节序列与code point的映射关系,那映射表里将有超过百万条记录。W3C将4-byte字节序列与code point的映射分为207个范围,每个范围有一个索引,索引是范围内起始的字节序列和起始的code 的映射关系。一个code point相对于索引的偏移与字节序列相对于索引的偏移是相同的[8][10]。
5.3.3 Big-5[11]#
5.3.3.1 Big-5简介
ASCII不支持中文、日文和韩文字符,政府和工业为了解决这个问题做出了很多努力。1984年,台湾信息行业研究所发布了Big-5。Big-5的诞生源于台湾最大的5家IT公司的合作,Acer、MiTAC、JiaJia、ZERO ONE Technology和First International Computer(FIC)。Big-5是一种中文繁体字符的编码方式,在台湾、香港和澳门等地区被使用。相比而言,中国内地使用简体字符编码方式GB18030。
Big5的设计不遵循ISO 2022标准,它是一种DBCS(double-byte character set)的编码方式。Big-5的编码空间为94u157,编码方式遵循下表的原则。Big5的标准版本的第一个和第二个字节不能使用字节0x7F、0xA0和0xFF。0xA1-0xFE范围内的代码可以用在第一个字节和第二个字节。0x40-0x7E范围内的代码既可以用在第二个字节,也可以用在单字节中。
表 4 Big5中字节编码值的范围
First byte ("lead byte") | 0x81 to 0xfe (or 0xa1 to 0xf9 for non-user-defined characters) |
---|---|
Second byte | 0x40 to 0x7e, 0xa1 to 0xfe |
严格来说,Big5仅包含DBCS字符。Big5通常会和SBCS(比如ASCII)一起使用,所以在Big5编码的文本中你可以同时看到DBCS和SBCS中的字符。0x00-0x7F的字节不被DBCS使用,被使用于SBCS。
一个Big5代码不总是代表一个完整的语义单元。比如,Big5代码0xa14b(…)对于说英语的人来说是省略号,但中文中的省略号应该是"......"。Big5中没有表示中文省略号的代码,要表示中文省略号要占用2个中文字符的空间,但很多DBCS系统要求一个DBCS字符只能占用一个中文字符的空间。这会带来一些问题。当我们在屏幕上编辑文本的时候,1-byte的字符占用屏幕上一个位置,DBCS(通常是2-byte)的字符占用屏幕上的两个位置。如果DBCS的字符即可用2-byte也可用4-byte表示,如果软件统一认为DBCS字符占用2个屏幕位置,就可能会引发错误。
不是所有的Big5字符都能用于纯文本文件。比如0xa1ca(﹋)应在文学作品的标题下使用;以科学计数法表示的苏州数字,需要以至少有2行的2维形式排列。
5.3.3.2 Big-5的扩展
通用的Big-5只包含常用字符表的4808个字符和非常用字符表的6363个字符,并不包含人名、地名、方言、化学、生物学和日文假名(Japanese kana)。因此,许多软件都使用了Big-5的扩展,以解决该问题。Big-5的扩展主要分为供应商扩展和官方扩展。
倚天(ETEN)是中国的操作系统,它定义了Big-5的供应商扩展,被称为倚天扩展。倚天扩展添加了以下代码点,并包含了IBM 5550的code page中的一些字符。倚天扩展的一些版本还额外添加了图形字符和简体中文字符。
- A3C0–A3E0: 33 control characters.
- C6A1–C875: circle 1–10, bracket 1–10, Roman numerals) 1–9 (i–ix), CJK radical glyphs, Japanese hiragana, Japanese katakana, Cyrillic characters
- F9D6–F9FE: the characters '碁', '銹', '恒', '裏', '墻', '粧' and '嫺', followed by 34 additional semigraphic symbols.
微软将Big-5扩展为Code page 950,以在Microsoft Windows上使用,它支持倚天扩展中F9D6-F9FE范围的代 码点。在一些版本中,欧元符号€被映射到Big-5的code point A3E1。你可以在中文的Windows操作系统(或Window2000的任意版本)上安装一个HKSCS 补丁,安装补丁后,使用code page 950的应用会自动使用隐藏的code page 951 table。code page 951 table支持 HKSCS-2001中除标准规定的兼容性code point外的所有code point。
HKSCS(Hong Kong Supplementary Character Set)是香港政府与1999年创建的字符集。在当时,粤语的书写有自己的字符,这些字符在Big-5中没有。为了解决这个问题,香港政府先后创建了Big-5的扩展GCCS(Government Chinese Character Set)和HKSCS。HKSCS包含倚天扩展的所有字符,以及一些简体中文、地名、人名和粤语短语的字符。最新的版本HKSCS-2016共收录了5,033个字符。HKSCS通过Big-5和ISO10646(Unicode)进行编码[13]。
Big5+是中文数位化技术推广委员会(Chinese Foundation for Digitization Technology)于1997年提出的。它使用超过20000个code point,将CJK所有象形文字纳入Unicode1.1。然而,额外的代码点超出了Big-5的限制(Big5+的第一个字节值使用了81-FE范围内的值,第二个字节值使用40-7E和80-FE范围内的值)。如果没有新的codepage 文件,它不能在Windows上安装。
5.3.3.3 Big-5与CNS11643的关系[12]
CNS11643字符集是中国台湾的官方标准,但事实上标准是Big-5的各种变体。CNS11643的设计遵循ISO2022标准。它有16个plane,编码空间为16u94u94,共141376个字符。
CNS 11643的plane 1和plane 2的字符取自Big-5的Level 1和Level 2,只是字符顺序不同以及其它轻微不同。它们可以通过范围列表进行相互映射。CNS11643后来又在Big-5的基础上添加了古典部首等字符。Big5-2003被定义为CNS11643的部分编码。
在Big-5汉字库里,只有一个字符与Unicode的映射与CNS 11643的头两个plane不同。这个字符在Big-5里映射到U+5F5D (彝),而在对应的CNS 11643的plane 1中,则映射到U+5F5E (彞)。在一些Big-5的变体中,比如IBM定义的一些变体以及Big5-2003中,收录了U+5F5E (彞))而不是U+5F5D (彝)。在Big5-2003中,代码点0xC255映射到Unicode的字符U+5F5E (彞)[14];在CNS11643中,代码点0x817641映射到Unicode的字符U+5F5E (彞)[15]。
Unicode1.0.0尽管不包含汉字,但包含CNS 11643中的字符,在Unicode1.0.0中对应于CJK Compatibility Forms块。CJK Compatibility Forms是指包含东亚的垂直字形变体的Unicode块,这个被称为“CNS 11643 Compatibility”,如表5。除此之外,当Unicode1.0.1在收录CJK Unified Ideographs后,它还受到了GB/T 12345-90和CNS 11463-1986的plane 14的影响[12][16]。
表5 Unicode1.0.0中的CJK Compatibility Forms块

参考资料:#
[2] https://en.wikipedia.org/wiki/Morse_code
[3] https://en.wikipedia.org/wiki/ISO/IEC_646
[4] https://en.wikipedia.org/wiki/Baudot_code
[5] https://en.wikipedia.org/wiki/Byte_order_mark
[7] https://en.wikipedia.org/wiki/GBK_(character_encoding)
[8] https://en.wikipedia.org/wiki/GB_18030
[9] CESI (2009-07-08). "GB18030 符合性问与答" [GB18030 compliance FAQ]. CESI Certification Center. Archived from the original on 2016-09-28. Retrieved 2016-10-12. Page 4
[10] 见"Encoding Standard # gb18030-index". WHATWG. Retrieved 2016-09-24
[11] https://en.wikipedia.org/wiki/Big5
[12]. https://en.wikipedia.org/wiki/CNS_11643
[13] https://moztw.org/docs/big5/table/hkscs2004.txt
[14] https://moztw.org/docs/big5/table/big5_2003-b2u.txt
[15] https://opensource.apple.com/source/ICU/ICU-3.13/icuSources/data/mappings/cns-11643-1992.ucm
[16] Lunde, Ken(2008). "3. Character Set Standards". CJKV Information Processing (2nd ed.). O'Reilly Media. ISBN[9780596514471]
1
作者:jann8
出处:https://www.cnblogs.com/jann8/p/16560099.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战