您的位置:首页 > 编程语言 > Java开发

Java中的字符集编码入门 1

2009-11-14 14:01 218 查看
Java中的字符集编码入门
ASCII及相关标准
  地球人都知道ASCII就是美国标准信息交换码的缩写,也知道ASCII规定用7位二进制数字来表示英文字符,ASCII被定为国际标准之后的
代号为ISO-646.由于ASCII码只使用了7个二进制位,也就是说一个字节可以表示的256个数字中,它仅使用了0-127这128个码位,剩下的
128个码位便可以用来做扩展,用来表示一些特定语言所独有的字符,因此对这多余的128个码位的不同扩展,就形成了一系列ISO-8859-*的标准。
例如为英语作了专门扩展的字符集编码标准编号为ISO-8859-1,也叫做Latin-1,为希腊语所作的扩展编号为ISO-8859-7等,完整的列
表可以参考《Java Internationalization》一书。
  Unicode与UCS
  整个Unicode项目是由多家计算机软件公司,还包括一些出版行业的公司共同发起的,从上世纪八十年代就已经开始。地球人都知道,对于日文,
汉字来说,256个码位是远远不够用的(当然,在当时并不是地球人都知道,起码设计计算机的老美们就不知道,甚至直到今天,还有老美以为米国是世界上唯一
的国家)。解决方法很直观也很明显,那就是采用码位多到足够包含所需字符数量的编码方案(即俗话说的头痛医头,脚痛医脚嘛)。这也是Unicode的目标
之一,能够包含世界上所有语言的字符(包括汉字,日文,数学符号,音乐符号,还包括各种奇奇怪怪看也看不懂的东西比如象形文字,甲骨文
,三个代表,科学发展观等等,笑),这个理想,可以说很远大,但很快被发现仅靠Unicode原先的设计无法实现。Unicode的另一个设计目标,对今
天影响深远,那就是对所有字符都采用16位编码(即用一个大小不超过2的16次方的整数数字给每个字符编号,注意从这个意义上也可以看出,Unicode
是一种编码字符集,而非字符集编码)。说这个设计目标对现今影响深远,完全不是表扬,因为到后来连Unicode的设计者也发现,16位编码仅有
65536个码位,远远不能容纳世界上所有的字符,但当意识到这个问题的时候,Unicode大部分的规范已经制定完毕,也有相当程度的普及,完全推倒重
来是不现实的。这成了一个遗留问题,也是surrogate pair(代用品)这种蹩脚解决方案的发端。
  无独有偶,在1984年,喜欢以繁多的编号糊弄群众的国际标准化组织ISO也开始着手制定解决不同语言字符数量太大问题的解决方案,这一方案被
称为Universal Character Set(UCS),正式的编号是ISO-10646(记得么,ASCII是ISO-646,不知这种安排是否是故意的)。还是ISO高瞻远瞩,一开始就确
定了UCS是一个31位的编码字符集(即用一个大小不超过2的31次方的整数数字为每个字符编号),这回真的足以容纳古往今来所有国家,所有语言所包含的
字符了(是的,任何国家,任何小语种都包括,也不管这些国家是与台湾建交还是与中国大陆建交,是拥护民主制度还是实行恐怖主义,所以说科学无国界)。虽然
后来他们意识到,2的31次方个码位又实在太多了……
  天下大势,分久必合。无论Unicode还是UCS,最初的目的都是杜绝各种各样名目繁多形式各异互不兼容老死不相往来的私用扩展编码(好啰嗦
的一句话),结果两方确立标准的同时(最初时这两个标准是不兼容的),又形成了割据,这对建设和谐社会是不利的,违反当今世界和平与发展的主旋律,中国政
府一向反对任何形式的霸权主义和强权政治,对以米国为首的发达国家……扯远了扯远了。1991年,Unicode联盟与ISO的工作组终于开始讨论
Unicode与UCS的合并问题,虽然其后的合并进行了很多年,Unicode初版规范中的很多编码都需要被改写,UCS也需要对码空间的使用进行必要限制,但成果是喜人的。最终,两者统一了抽象字符集(即任何一个在Unicode中存在的字符,在UCS中也存在),且最靠前的65535个字符也统一了字符的编码。对于码空间,两者同意以一百一十万为限(即两者都认为虽然65536不够,但2的31次方又太大,一百一十万是个双方都可接受的码空间大小,也够用,当然,这里说的一百一十万只是个约数),Unicode将码空间
展到了一百一十万,而UCS将永久性的不使用一百一十万以后的码位。也就是说,现在再讲Unicode只包含65536个字符是不对的。除了对已经定义的
字符进行统一外,Unicode联盟与ISO工作组也同意今后任何的扩展工作两者均保持同步,因此虽然从历史的意义上讲Unicode与UCS不是一回事
(甚至细节上说也不是一回事),但现在提起Unicode,指代两者均无不妥。何的扩展工作两者均保持同步,因此虽然从历史的意义上讲Unicode与
UCS不是一回事(甚至细节上说也不是一回事),但现在提起Unicode,指代两者均无不妥。

需要再一次强调的是,无论历史上的UCS还是现如今的Unicode,两者指的都是编码字符集,而不是字符集编码。花费一点时间来理解好这件事,然后你会发现对所有网页的,系统的,编码标准之间的来回转换等等繁杂事务都会思路清晰,手到擒来。
  首先说说最一般意义上的字符集。
  一个抽象字符集其实就是指字符的集合,例如所有的英文字母是一个抽象字符集,所有的汉字是一个抽象字符集,当然,把全世界所有语言的符号都放在
一起,也可以称为一个抽象字符集,所以这个划分是相当人为的。之所以说“抽象”二字,是因为这里所提及的字符不是任何具体形式的字符,拿汉字中的“汉”这
个字符来说,您在这篇文章中看到的这个“汉”其实是这个字符的一种具体表现形式,是它的图像表现形式,而且它是用中文(而非拼音)书写而成,使用宋体外
观;而当人们用嘴发出“汉”这个音的时候,他们是在使用“汉”的另一种具体表现形式——声音,但无论如何,两者所指的字符都是“汉”这个字。同一个字符的
表现形式可能有无数种(点阵表示,矢量表示,音频表示,楷体,草书等等等等),把每一种表现形式下的同一个字符都纳入到字符集中,会使得集合过于庞大,冗
余高,也不好管理。因此抽象字符集中的字符,都是指唯一存在的抽象字符,而忽略它的具体表现形式。
  抽象字符集中的诸多字符,没有顺序之分,谁也不能说哪个字符在哪个字符前面,而且这种抽象字符只有人能理解。在给一个抽象字符集合中的每个字符
都分配一个整数编号之后(注意这个整数并没有要求大小),这个字符集就有了顺序,就成为了编码字符集。同时,通过这个编号,可以唯一确定到底指的是哪一个
字符。当然,对于同一个字符,不同的字符集编码系统所制定的整数编号也不尽相同,例如“儿”这个字,在Unicode中,它的编号是0x513F,(为方
便起见,以十六进制表示,但这个整数编号并不要求必须是以十六进制表示)意思是说它是Unicode这个编码字符集中的第0x513F个字符。而在另一种
编码字符集比如Big5中,这个字就是第0xA449个字符了。这种情况的另一面是,许多字符在不同的编码字符集中被分配了相同的整数编号,例如英文字母
“A”,在ASCII及Unicode中,均是第0x41个字符。我们常说的Unicode字符集,指的就是这种被分配了整数编号的字符集合,但要澄清的
是,编码字符集中字符被分配的整数编号,不一定就是该字符在计算机中存储时所使用的值,计算机中存储的字符到底使用什么二进制整数值来表示,是由下面将要
说到的字符集编码决定的。
  字符集编码决定了如何将一个字符的整数编号对应到一个二进制的整数值,有的编码方案简单的将该整数值直接作为其在计算机中的表示而存储,例如英
文字符就是这样,几乎所有的字符集编码方案中,英文字母的整数编号与其在计算机内部存储的二进制形式都一致。但有的编码方案,例如适用于Unicode字
符集的UTF-8编码形式,就将很大一部分字符的整数编号作了变换后存储在计算机中。以“汉”字为例,“汉”的Unicode值为0x6C49,但其编码
为UTF-8格式后的值为0xE6B189(注意到变成了三个字节)。这里只是举个例子,关于UTF-8的详细编码规则可以参看《Mapping
codepoints to Unicode encoding forms》一文,URL为http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&
amp;item_id=IWS-AppendixA#sec3.我们经常听说的另一种编码方案UTF-16,则对Unicode中的前65536个字符
编号都不做变换,直接作为计算机存储时使用的值(对65536以后的字符,仍然要做变换),例如“汉”字的Unicode编号为0x6C49,那么经过
UTF-16编码后存储在计算机上时,它的表示仍为0x6C49!。我猜,正是因为UTF-16的存在,使得很多人认为Unicode是一种编码(实际
上,是一个字符集,再次重申),也因此,很多人说Unicode的时候,他们实际上指的是UTF-16.UTF-16提供了surrogate pair
机制,使得Unicode中码位大于65536的那些字符得以表示。
  Surrogate pair机制在目前来说实在不常用,甚至连一些UTF-16的实现都不支持,所以我不打算在这里多加讨论,其基本的思想就是用两个16位的编码表示一个字
符(注意,只对码位超过65536的字符这么做)。Unicode如此死抱着16这个数字不放,有历史的原因,也有实用的原因。
  当然还有一种最强的编码,UTF-32,他对所有的Unicode字符均不做变换,直接使用编号存储!(俗称的以不变应万变),只是这种编码方案太浪费存储空间(就连1个字节就可以搞定的英文字符,它都必须使用4个字节),因而尽管使用起来方便(不需要任何转换),却没有得到普及。
  记得当初Unicode与UCS还没成家之时,UCS也是需要人爱,需要人疼的,没有自己的字符集编码怎么成。UCS-2与UCS-4就扮演了
这样的角色。UCS-4与UTF-32除了名字不同以外,思想完全一样。而UCS-2与UTF-16在对前65536个字符的处理上也完全相同,唯一的区
别只在于UCS-2
不支持surrogate pair机制,即是说,UCS-2只能对前65536个字符编码,对其后的字符毫无办法。不过现在再谈起字符编码的时候,UCS-2与UCS-4早已成为
计算机史学家才会用到的词汇,就让它们继续留在故纸堆里吧。

GB2312是对中国的开发人员来说很重要的一个词汇,它的来龙去脉并不需要我在这里赘述,随便Goolge之便明白无误。我只是想提一句,记得前一节说到编码字符集和字符集编码不是一回事,而有的字符集编码又实际上没有做任何事,GB2312正是这样一种东西!
  GB2312最初指的是一个编码字符集,其中包含了ASCII所包含的英文字符,同时加入了6763个简体汉字以及其他一些ASCII之外的符
号。与Unicode有UTF-8和UTF-16一样(当然,
UTF-8和UTF-16也没有被限定只能用来对Unicode进行编码,实际上,你用它对视频
行编码都是可以的,只是编出的文件没有播放器支持罢了,哈哈),GB2312也有自己的编码方案,但这个方案直接使用一个字符在GB2312中的编号作为
存储值(与UTF-32的做法类似),也因此,这个编码方案甚至没有正式的名称。我们日常说起GB2312的时候,常常即指这个字符集,也指这种编码方
案。
  GBK是GB2312的后续标准,添加了更多的汉字和特殊符号,类似的是,GBK也是同时指他的字符集和他的编码。
  GBK还是现如今中文Windows操作系统的系统默认编码(这正是几乎所有网页上的,文件里的乱码问题的根源)。
  我们可以这样来验证,使用以下的Java代码:

String encoding=System.getProperty("file.encoding");

System.out.println(encoding);
输出结果为GBK
Charset encoding=Charset.defaultCharset();

System.out.println(encoding);
输出结果为GBK
  (什么?你的输出不是这样?怎么可能?完了,我的牌子要砸了,等等,你用的繁体版XP?我说你这同志在这里捣什么乱?去!去!)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: