Unicode定义了一个统一表示全世界所有文本的多语言文本编码方式。为了述说方便,就不区分Unicode组织和国际标准 ISO 10646 了,起初它们是在不知道对方的情况下,分别独立制定一套可以统一世界所有文字的编码方案。不久它们就发现了对方的存在,并认为世界不需要两个不一样的统一通用的字符集。所以他们联合起来维护一个字符集,我们就统称它们是Unicode吧,虽然这两个机构现在依然都存在,但是它们制定的方案是互相兼容的。
下面这RFC3629中的一段原话,清楚地表明了它们二者是统一和一致的。
ISO/IEC 10646 [ISO.10646] defines a large character set called the
Universal Character Set (UCS), which encompasses most of the world's
writing systems. The same set of characters is defined by the
Unicode standard [UNICODE], which further defines additional
character properties and other application details of great interest
to implementers. Up to the present time, changes in Unicode and
amendments and additions to ISO/IEC 10646 have tracked each other, so
that the character repertoires and code point assignments have
remained in sync. The relevant standardization committees have
committed to maintain this very useful synchronism.
ISO/IEC 10646不断地通过修正案和附录在更新着UCS,与此同时,新版本的Unicode也在不断推出。
在1996年,ISO 10646的1993版本和Unicode2.0版本做了一些影响很不好的变更。通常情况下,往未分配代码点的区域添加一定数量的新字符并不会影响原有的字符编码。但是委员会的人移动和扩充了韩文音节那一块,而不是只是向里面新添加内容。这导致了Unicode2.0版本与之前的版本,韩文不兼容的情况的出现。这一事件被称为“Korean mess”,相关的委员会对此作出明确保证,不兼容的情况从此之后绝对不会再出现了。 也就是说,从Unicode2.0版本开始,所有编码都是向前兼容的。
国际标准 ISO 10646 定义了通用字符集 (Universal Character Set,UCS)在起初的时候,人们认为只用2个字节就可以表示所有的字符了,所以起初的编码方案又被称为UCS-2,顾名思义,UCS-2就是用两个字节编码。随着研究的深入,渐渐认识到2个字节不足以表达全部的字符文字,而3个字节是足够的,可以表示一千六百多万个字符。然而,为了计算机的处理和存储方便,采用了4个字节来进行方案编码,这就有了UCS-4,顾名思义,UCS-4是用4个字节编码的。
现在Unicode名义上是4个字节编码,但是实际使用的只是0x0~0x10FFFF这个范围。因为这已足够表示所有的字符了。
Unicode 编码系统,可分为编码方式和实现方式两个层次。实现方式主要有UTF-8,UTF-16和UTF-32,还有UTF-7。而本篇文章的主题是编码方式。至于实现方式,留到本系列后面几篇文章中去详述。
编码方式:对于一个特定的字符,Unicode用一个4个字节的数字来表示它。我们称这个数字为Unicode代码点(code point),简称代码点。
再解释一遍,由于全世界从古到今以至未来,所有的文字符号加起来也不会超过一百万个,所以Unicode规定编码字符的代码点范围只用从0到0x10FFFF来表示。也就是说,Unicode的编码方式只用了两个字节多,不到三个字节,确切地说,用了21位(bit)。注意,这是Unicode的编码方式,不是实现方式。
//Unicode的代码空间
0x0~0x10FFFF
由此我们可以计算出Unicode的可用代码点的数量:
显然有两个区间段要计算,第一个是0x0~0xFFFFF;第二个是0x100000~0x10FFFF。
第一个区间段为2^20(2的20次方),即1048576个;第二个区间段为2^16(2的16次方),即65536个。二者相加为1114112个,这是一百多万个代码点,足以装下整个世界。
对于代码点,通常用U+ 后跟十六进制数字表示。对于同一个字符,Unicode用同一个代码点来表示。比如下面的“骨”这个汉字,其代码点为U+9AA8,这是唯一固定的。
如下图所示:
上图是CJKV象形文字“骨”的例子,体现了字体和字体的重要差别,这些区别在中国和日本比较明显。但是,这些形式都被认为是同一个字,在Unicode中代码点均为U+9AA8。
这就清楚地表明了字符本身与对应的字体之间的区别。
当然,对于显示出来的字符,有时候是由两个甚至多个字符合成的。比如下图,图中的数字为十六进制表示的代码点,这是由两个字符动态合成的:
通过上面的讲解,我们知道了Unicode代码点空间由数值范围从0到0x10FFFF的区域组成。但是,其中有66个代码点是不可用于对字符进行编码的。这些代码点包括:
U+FDD0...U+FDEF
以0xFFFE或0xFFFF结尾的代码点,即U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, ... U+10FFFE, U+10FFFF。
注意,不要与表示BOM的代码点U+FEFF混淆了,U+FEFF是一个合法的代码点。而上面的66个是非法的,是不存在的。正因为U+FEFF是合法代码点,面U+FFFE是不存在的。所以当字符流开头只要出现 FF FE 或者 FE FF 这样的字节序列,就可以认为它是U+FEFF,并且可以判断出是Big Endian还是Little Endian了。
为了方便管理及表述,Unicode组织将代码点空间分成了6个字符平面(Planes)。上面讲过,Unicode用了21位来编码所有字符。抛开低位的两个字节,高位还剩下5bit,我们使用这5位来编码每个平面。我们知道,对于高位的0x00~0x10,可以编码17个平面。但是Unicode7.0只用了6个平面,并且命名了其中的4个。它们分别是0号平面、1号平面、2号平面、14号平面、15号平面、16号平面。每个平面都有64K个(也就是2的16次方个)代码点组成。也就是说,对于每个代码点,它低位的四个十六进制数字表明一个字符在平面中的位置,而其余的数字用来表示平面。举个例子,U+23456表示的代码点在2号平面的0x3456位置上。
- 对于0号平面,其代码点范围是0x0~0xFFFF。对于0号平面,Unicode也称它为基本多语言平面(Basic Multilingual Plane,BMP)。BMP包含了所有普遍使用的字符,以及许多旧的不常见的字符。迄今为止,绝大多数的文本数据使用的Unicode字符都可以在BMP中找到。
- 对于1号平面,其代码点范围是0x10000~0x1FFFF。对于1号平面,Unicode称它为补充的多语言平面(Supplementary Multilingual Plane,SMP)。SMP是专门用来编码较少使用的古文字、特殊目的生成的文字和符号系统具体例子包括Gothic、Shavian和音乐符号。
- 对于2号平面,其代码点范围是0x20000~0x2FFFF。对于1号平面,Unicode称它为补充的象形文字平面(Supplementary Ideographic Plane,SIP)。SIP是专门为那些CJK字符提供的一种额外的分配区域,因为这些CJK字符不适合放在为BMP中的一般CJK字符预留的区域中。绝大多数SIP中的字符都是极其罕见的。
- 对于14号平面,其代码点范围是0xE0000~0xEFFFF。对于1号平面,Unicode称它为补充的特殊用途平面(Supplementary Special-purpose Plane,SSP)。SSP是为格式控制字符设置的溢出分配区域,这些格式控制字符在BMP中不能作为格式控制字符分配到小的区域中去。
- 对于15和16号平面,其代码点范围分别是0xF0000~0xFFFFF和0x100000~0x10FFFF。对于15号和16号平面,Unicode称它们为自行使用平面(Private Use Planes)。这两个平面从其整体上作为私有变量来使用。这两个平面总共包含131068个字符,以供自定义使用,从而补充了在BMP中供自定义使用的6400个私用字符。
下面我们来看一下BMP的基本结构。
从上图我们可以看到,BMP中划分了很多分配区域。我们来依次介绍一下。
- 常用文字区域:常用文字区域包含了大量现代使用的文字,有拉丁文、希腊文、西里尔文、阿拉伯文等。这个区域编码的大多数字符是图形字符。但所有65C0和C1控制码也分配在这里,这是因为要兼容ASCII码和ISO/IEC 8859-1标准。
- 符号区:符号区包含了各种符号,其中有许多用于数学符号的字符,同时也包含了标点符号以及大多数重要的格式控制字符。
- CJK混合区域(CJK Miscellaneous Area):CJK混合区域包含了一些东亚文字,如日语中的平假名和片假名,通常用于东亚文字的标点、CJK基本符号列表以及大量的东亚兼容字符。
- CJKV象形文字区域(CJKV Unified Ideographs Area):这个区域包含了所有在BMP中统一的汉字。统一指这个区域被划分为指令系统和命令块(初始块中的20902个统一汉字和22个增补字符),以及另一个包含扩展A块(额外的6582个韩文统一象形字符)。
- 亚非文字区域(General Scripts Area (Asia and Africa)):目前包括彝文、八思巴文以及11172个朝鲜文音节。
- 替代区域(Surrogates Area):只含有替代编码点,并没有实际指定编码字符。
- 自行使用区域(Private Use Area):这就是上面所说的BMP中的那6400个可以自定义使用的字符。
- 兼容和特定区域(Compatibility and Specials Area):这个区域包含了很多兼容的字符变体,它们是Unicode标准中广泛使用的行业及国家标准的其他形式。例如,其中包含了阿拉伯文表现形式,但阿拉伯文的基本字符位于常用文字区域。这个区域还包含了几个重要的格式控制字符和其他特殊字符。
Unicode的编码方式基本就介绍完了。下一篇本系列的文章开始介绍UTF-8。