Knowledge Point 20180305 Java程序员详述编码Unicode Unicode

   Unicode统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求Unicode的作用。1990年开始研发,1994年正式公布。

Unicode出现的原因

  在Unicode出现之前,已经存在很多不同的标准了,如美国的ASCII、西欧的ISO 8859-1、俄罗斯的KOI-8、中国的GB 18030BIG 5等。这样就会产生下面两个问题:一个对于任意给定字符值,在不同的编码方案下有可能对应不同的字母;二是采用大字符集的语言其编码长度有可能不同。例如,有些常用的字符采用单字节编码,而另一些字符则需要两个或更多个字节。

  设计Unicode是为了解决上面传统的字符编码方案的局限而产生的。在20世纪80年代开始启动设计工作时,人们认为两个字节的代码宽度足以对当时世界上各种语言的所有字符进行编码,并有足够的空间留给未来的扩展(在这里只能说so young so simple,外国人万万想不到我大中华有56个民族吧,万万想不到我们汉文化的博大精深吧,我们不仅有简体字,我们还有繁体字,NB的是我们还有甲骨文,你说就给我们俩字节,这不说升级呢吗?)。

  在1991年发布Unicode1.0版本的时候,当时仅用了65536个代码值中不到一半的部分。当时Java正处于萌芽状态,Java的设计决定采用16为的Unicode字符集,这样会比使用其它8位字符集的程度设计语言有很大的改进。

Unicode爆了

  但好景不长,上面我们所调侃的状态就出现了Unicode字符超出了65536个,现在16为的char已经不能满足描述所有Unicode字符的需要了。

  我们知道字符存储在计算机中是依赖于编码表对其进行的翻译,编码表会找出这个字符在表中对应的数字,然后将这个数字存储在计算机中,这个过程就是编码的过程。当我们读取该字符时,进行一次对照的解码,将数字对应的字符返还给我们,这是解码的过程(所以编码和解码应该使用相同的编码集,否则会产生乱码的情形)。在这里我们要着重介绍的是字符对应编码表中的数字,这个数字就是码点(code point),也叫点代码。码点:是指一个编码表中的某个字符对应的代码值。(了解码点对于以后Java中一些关于码点的方法有帮助)

  在Unicode标准中,码点采用16进制书写,并加上前缀U+,例如U+0041就是拉丁字母A的码点。Unicode为了解决不能满足字符需求的情况,将码点分为了17个代码等级(code plane),0x0000 0x10FFFF每组称为平面(Plane),而每平面拥有65536个码位,共1114112。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。第一个代码等级称为基本的多语言级别(basic multilingual plane),码点从U+0000U+FFFF,其中就包括了我们常见的经典的Unicode代码,其余的16个码点级别从U+10000U+10FFFF,其中包括一些辅助字符(supplementary character)。

  上面提到了UTF-16,这是很有必要讲解一下的。UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者216位长的码元来表示,因此这是一个变长表示。

  在基本的多语言级别中,每个字符用16为来表示,通常被称为代码单元(code unit);而辅助字符采用一对连续的代码单元进行编码。这样构成的编码值落入基本的多语言级别中空闲的2048字节内,通常被称作替代区域(surrogate area)【U+D800 ~U+DBFF 用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元】。这样的设计十分巧妙,可以从中迅速知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。例如,下面字符

 Knowledge Point 20180305 Java程序员详述编码Unicode
Unicode

是一个八元数集的一个数学符号,码点为U+1D546,编码为两个代码单元U+D835U+DD46。我们通过代码单元可以很容易看出来这是一个辅助字符。

  在Java中使用Unicode作为默认的编码集,char类型描述了UTF-16编码中的一个代码单元(这也就是为什么char需要两个字节的存储空间了)。在Java中强烈建议不在程序中使用char类型,除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型处理。