Dec 24

Java中的字符集编码入门 不指定

(一)Unicode与UCS的历史恩怨
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,指代两者均无不妥。

(二)编码字符集与字符集编码的区别
需要再一次强调的是,无论历史上的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...
我们经常听说的另一种编码方案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和GBK。

(三)GB2312,GBK与中文网页
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
(什么?你的输出不是这样?怎么可能?完了,我的牌子要砸了,等等,你用的繁体版XP?我说你这同志在这里捣什么乱?去!去!)
说到GB2312和GBK就不得不提中文网页的编码。尽管很多新开发的Web系统和新上线的注重国际化的网站都开始使用UTF-8,仍有相当一部分的中文媒体坚持使用GB2312和GBK,例如新浪的页面。其中有两点很值得注意。
第一,页面中meta标签的部分,常常可以见到
charset=GB2312
这样的写法,很不幸的是,这个"charset"其实是用来指定页面使用的是什么字符集编码,而不是使用什么字符集。例如你见到过有人写 "charset=UTF-8",见到过有人写"charset=ISO-8859-1",但你见过有人写"charset=Unicode"么?当然没有,因为Unicode是一个字符集,而不是编码。
然而正是charset这个名称误导了很多程序员,真的以为这里要指定的是字符集,也因而使他们进一步的误以为UTF-8和UTF-16是一种字符集!(万恶啊)好在XML中已经做出了修改,这个位置改成了正确的名称:encoding。
第二,页面中说的GB2312,实际上并不真的是GB2312(惊讶么?)。我们来做个实验,例如找一个GB2312中不存在的汉字"亸"(这个字确实不在GB2312中,你可以到GB2312的码表中去找,保证找不到),这个字在GBK中。然后你把它放到一个html页面中,试着在浏览器中打开它,然后选择浏览器的编码为"GB2312",看到了什么?它完全正常显示!
结论不用我说你也明白了,浏览器实际上使用的是GBK来显示。
新浪的页面中也有很多这样的例子,到处都写charset=GB2312,却使用了无数个GB2312中并不存在的字符。这种做法对浏览器显示页面并不成问题,但在需要程序抓取页面并保存的时候带来了麻烦,程序将不能依据页面所"声称"的编码进行读取和保存,而只能尽量猜测正确的编码。

(四)网页文件的编码
接着上节的思路说,一个网页要想在浏览器中能够正确显示,需要在三个地方保持编码的一致:网页文件,网页编码声明和浏览器编码设置。
首先是网页文件本身的编码,即网页文件在被创建的时候使用什么编码来保存。这个完全取决于创建该网页的人员使用了什么编码保存,而进一步的取决于该人员使用的操作系统。例如我们使用的中文版WindowsXP系统,当你新建一个文本文件,写入一些内容,并按下ctrl+s进行保存的那一刻,操作系统就替你使用GBK编码将文件进行了保存(没有使用UTF-8,也没有使用UTF-16)。而使用了英文系统的人,系统会使用ISO-8859-1进行保存,这也意味着,在英文系统的文件中如果输入一个汉字,是无法进行保存的(当然,你甚至都无法输入)。
一个在创建XML文件时(创建HTML的时候倒很少有人这么做)常见的误解是以为只要在页面的encoding部分声明了UTF-8,则文件就会被保存为 UTF-8格式。这实在是……怎么说呢,不能埋怨大家。实际上XML文件中encoding部分与HTML文件中的charset中一样,只是告诉"别人 "(这个别人可能是浏览你的页面的人,可能是浏览器,也可能是处理你页面的程序,别人需要知道这个,因为除非你告诉他们,否则谁也猜不出你用了什么编码,仅通过文件的内容判断不出使用了什么编码,这是真的)这个文件使用了什么编码,唯独操作系统不会搭理,它仍然会按自己默认的编码方式保存文件(再一次的,在我们的中文WindowsXP系统中,使用GBK保存)。至于这个文件是不是真的是encoding或者charset所声明的那种编码保存的呢?答案是不一定!
例如新浪的页面就"声称"他是用GB2312编码保存的,但实际上却是GBK,也有无数的二把刀程序员用系统默认的GBK保存了他们的XML文件,却在他们的encoding中信誓旦旦的说是UTF-8的。
这就是我们所说的第二个位置,网页编码声明中的编码应该与网页文件保存时使用的编码一致。
而浏览器的编码设置实际上并不严格,就像我们第三节所说的那样,在浏览器中选择使用GB2312来查看,它实际上仍然会使用GBK进行。而且浏览器还有这样一种好习惯,即它会尽量猜测使用什么编码查看最合适。
我要重申的是,网页文件的编码和网页文件中声明的编码保持一致,这是一个极好的建议(值得遵循,会与人方便,与己方便),但如果不一致,只要网页文件的编码与浏览器的编码设置一致,也是可以正确显示的。
例如有这样一个页面,它使用GBK保存,但声明自己是UTF-8的。这个时候用浏览器打开它,首先会看到乱码,因为这个页面"告诉"浏览器用UTF-8显示,浏览器会很尊重这个提示,于是乱码一片。但当手工把浏览器设为GBK之后,显示正常。
说了以上四节这么多,后面我们就来侃侃Java里的字符编码,你会发现有意思且挠头的事情很多,但一旦弄通,天下无敌(不过不要像东方不败那样才好)。

(五)Java代码中的字符编码转换Part 1
如果你是JVM的设计者,让你来决定JVM中所有字符的表示形式,你会不会允许使用各种编码方式的字符并存?
我想你的答案是不会,如果在内存中的Java字符可以以GB2312,UTF-16,BIG5等各种编码形式存在,那么对开发者来说,连进行最基本的字符串打印、连接等操作都会寸步难行。例如一个GB2312的字符串后面连接一个UTF-8的字符串,那么连接后的最终结果应该是什么编码的呢?你选哪一个都没有道理。
因此牢记下面这句话,这也是Java开发者的共同意志:在Java中,字符只以一种形式存在,那就是Unicode(注意到我们没有选择特定的编码,直接使用它们在字符集中的编号,这是统一的唯一方法)。
但"在Java中"到底是指在哪里呢?就是指在JVM中,在内存中,在你的代码里声明的每一个char,String类型的变量中。例如你在程序中这样写

char han = ' ' ;

在内存的相应区域,这个字符就表示为0x6C49。可以用下面的代码证明一下:

char han = ' ' ;
System.out.format(
" %x " ,( short )han);

输出是:6c49
反过来用Unicode编号来指定一个字符也可以,像这样:

char han = 0x6c49 ;
System.out.println(han);

输出是:汉
这其实也是说,只要你正确的读入了"汉"这个字,那么它在内存中的表示形式一定是0x6C49,没有任何其他的值能代表这个字(当然,如果你读错了,那结果是什么就不知道了,范伟说:读,读错了呀,那还等于好几亿呢;本山大哥说:好几亿你也没答上,请听下一题)。
JVM的这种约定使得一个字符存在的世界分为了两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方案(也有一种很特殊的情况,使得在JVM内部也需要转换,不过这个是后话)。
因此可以说,所有的编码转换就只发生在边界的地方,JVM和OS的交界处,也就是你的各种输入输出流(或者Reader,Writer类)起作用的地方。
话头扯到这里就必须接着说Java的IO系统。
尽管看上去混乱繁杂,但是所有的IO基本上可以分为两大阵营:面向字符的Reader啊Wrtier啊,以及面向字节的输入输出流。
下面我来逐一分解,其实一点也不难。
面向字符和面向字节中的所谓"面向"什么,是指这些类在处理输入输入的时候,在哪个意义上保持一致。如果面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容要一致。不能变换任何0和1的顺序。因此这是一种非常"忠实于原著"的做法(偶然间让我想起郭敬明抄袭庄羽的文章,那家伙,太忠实于原著了,笑)。
这种输入输出方式很适合读入视频文件或者音频文件,或者任何不需要做变换的文件内容。
而面向字符的IO是指希望系统中的文件的字符和读入内存的"字符"(注意和字节的区别)要一致。例如我们的中文版WindowsXP系统上有一个GBK的文本文件,其中有一个"汉"字,这个字的GBK编码是0xBABA(而Unicode编号是0x6C49),当我们使用面向字符的IO把它读入内存并保存在一个char型变量中时,我希望IO系统不要傻傻的直接把0xBABA放到这个char型变量中,我甚至都不关心这个char型变量具体的二进制内容到底是多少,我只希望这个字符读进来之后仍然是"汉"这个字。
从这个意义上也可以看出,面向字符的IO类,也就是Reader和Writer类,实际上隐式的为我们做了编码转换,在输出时,将内存中的Unicode 字符使用系统默认的编码方式进行了编码,而在输入时,将文件系统中已经编码过的字符使用默认编码方案进行了还原。我两次提到"默认",是说Reader和 Writer的聪明也仅此而已了,它们只会使用这个默认的编码来做转换,你不能为一个Reader或者Writer指定转换时使用的编码。这也意味着,如果你使用中文版WindowsXP系统,而上面存放了一个UTF-8编码的文件,当你使用Reader类来读入的时候,它会傻傻的使用GBK来做转换,转换后的内容当然驴唇不对马嘴!
这种笨,有时候其实是一种傻瓜式的功能提供方式,对大多数初级用户(以及不需要跨平台的高级用户)来说反而是件好事。
但我们不一样啦,我们都是国家栋梁,肩负着赶英超美的责任,必须师夷长技以治夷,所以我们总还要和GBK编码以外的文件打交道。
说了上面这些内容,想必聪明的读者已经看出来,所谓编码转换就是一个字符与字节之间的转换,因此Java的IO系统中能够指定转换编码的地方,也就在字符与字节转换的地方,那就是(读者:InputSteamReader和OutputStreamWriter!作者:太强了,都会抢答了!)
这两个类是字节流和字符流之间的适配器类,因此他们肩负着编码转换的任务简直太自然啦!要注意,实际上也只能在这两类实例化的时候指定编码,是不是很好记呢?
下面来写一段小程序,来把"汉"字用我们非常崇拜的UTF-8编码写到文件中!

try {
PrintWriter out
= new PrintWriter( new OutputStreamWriter( new FileOutputStream( " c:/utf-8.txt " ), " UTF-8 " ));
try {
out.write(
" " );
}
finally {
out.close();
}
}
catch (IOException e){
throw new RuntimeException(e);
}

运行之后到c盘下去找utf-8.txt这个文件,用UltraEdit打开,使用16进制查看,看到了什么?它的值是0xE6B189!噢耶!(读者:这,这有什么好高兴的……)
下一节我们来看看实现这种操作的其他方式,读到这里,你已经基本上是字符编码的高手了哦。

(六)Java中的增补字符
Java号称对Unicode提供天然的支持,这话在很久很久以前就已经是假的了(不过曾经是真的),实际上,到JDK5.0为止Java才算刚刚跟上Unicode的脚步,开始提供对增补字符的支持。
现在的Unicode码空间为U+0000到U+10FFFF,一共1114112个码位,其中只有1,112,064 个码位是合法的(我来替你做算术,有2048个码位不合法),但并不是说现在的Unicode就有这么多个字符了,实际上其中很多码位还是空闲的,到 Unicode 4.0 规范为止,只有96,382个码位被分配了字符(但无论如何,仍比很多人认为的65536个字符要多得多了)。其中U+0000 到U+FFFF的部分被称为基本多语言面(Basic Multilingual Plane,BMP)。U+10000及以上的字符称为补充字符。在Java中(Java1.5之后),补充字符使用两个char型变量来表示,这两个 char型变量就组成了所谓的surrogate pair(在底层实际上是使用一个int进行表示的)。第一个char型变量的范围称为"高代理部分"(high-surrogates range,从"uD800到"uDBFF,共1024个码位), 第二个char型变量的范围称为low-surrogates range(从"uDC00到"uDFFF,共1024个码位),这样使用surrogate pair可以表示的字符数一共是1024的平方计1048576个,加上BMP的65536个码位,去掉2048个非法的码位,正好是1,112,064 个码位。

关于Unicode的码空间实际上有一些稍不小心就会让人犯错的地方。比如我们都知道从U+0000到U+FFFF的部分被称为基本多语言面(Basic Multilingual Plane,BMP),这个范围内的字符在使用UTF-16编码时,只需要一个char型变量就可以保存。仔细看看这个范围,应该有65536这么大,因此你会说单字节的UTF-16编码能够表示65536个字符,你也会说Unicode的基本多语言面包含65536个字符,但是再想想刚才说过的surrogate pair,一个UTF-16表示的增补字符(再一次的,需要两个char型变量才能表示的字符)怎样才能被正确的识别为增补字符,而不是两个普通的字符呢?答案你也知道,就是通过看它的第一个char是不是在高代理范围内,第二个char是不是在低代理范围内来决定,这也意味着,高代理和低代理所占的共 2048个码位(从0xD800到0xDFFF)是不能分配给其他字符的。
但这是对UTF-16这种编码方法而言,而对Unicode这样的字符集呢?在Unicode的编号中,U+D800到U+DFFF是否有字符分配?答案是也没有!这是典型的字符集为方便编码方法而做的安排(你问他们这么做的目的?当然是希望基本多语言面中的字符和一个char型的UTF-16编码的字符能够一一对应,少些麻烦,从中我们也能看出UTF-16与Unicode间很深的渊源与结合)。也就是说,无论Unicode还是UTF-16编码后的字符,在0x0000至0xFFFF这个范围内,只有63488个字符。这就好比最初的CPU被勉强拿来做多媒体应用,用得多了,CPU就不得不修正自己从硬件上对多媒体应用提供支持了。

尽管不情愿,但说到这里总还得扯扯相关的概念:代码点和代码单元。
代码点(Code Point)就是指Unicode中为字符分配的编号,一个字符只占一个代码点,例如我们说到字符"汉",它的代码点是U+6C49。
代码单元(Code Unit)则是针对编码方法而言,它指的是编码方法中对一个字符编码以后所占的最小存储单元。例如UTF-8中,代码单元是一个字节,因为一个字符可以被编码为1 个,2个或者3个4个字节;在UTF-16中,代码单元变成了两个字节(就是一个char),因为一个字符可以被编码为1个或2个char(你找不到比一个char还小的UTF-16编码的字符,嘿嘿)。说得再罗嗦一点,一个字符,仅仅对应一个代码点,但却可能有多个代码单元(即可能被编码为2个 char)。
以上概念绝非学术化的绕口令,这意味着当你想以一种统一的方式指定自己使用什么字符的时候,使用代码点(即你告诉你的程序,你要用Unicode中的第几个字符)总是比使用代码单元更好(因为这样做的话你还得区分情况,有时候提供一个16进制数字,有时候要提供两个)。
例如我们有一个增补字符???(哈哈,你看到了三个问号对吧?因为我的系统显示不出这个字符),它在Unicode中的编号是U+2F81A,当在程序中需要使用这个字符的时候,就可以这样来写:

String s=String.valueOf(Character.toChars(0x2F81A));
char[]chars=s.toCharArray();
for(char c:chars){
System.out.format(
"%x",(short)c);
}

后面的for循环把这个字符的UTF-16编码打印了出来,结果是
d87edc1a
注意到了吗?这个字符变成了两个char型变量,其中0xd87e就是高代理部分的值,0xdc1a就是低代理的值。


最后编辑: 毫无逻辑 编辑于2009/03/23 16:52
boli8p7n3303 Email
2010/09/09 15:07
深圳加油,中国加油!!!
hg1gdh7g6hhg Email Homepage
2010/09/07 21:12
十年以前听刘德华的歌,大家说我很前卫;十年以后再听刘德华的歌,大家说,哦,原来你是个这么怀旧的人啊。刘德华是谁???楼下邻居家小妹问我。
    
    十年以前别人的笑话常常让我捧腹大笑;十年以后只有领导的笑话才能让我捧腹大笑??即使已经听他说过八遍了。
    
    十年以前我以为我的生活至少会有500种可能;十年以后我知道我的生活只有2种可能??晚上回家吃饭和晚上不回家吃饭。
    
    十年以前别人总是对我很愤怒,就好像我一直是个不良少年一样;十年以后我总是对别人很愤怒,就好像我一直是个良好少年一样。
    
    十年以前在街头看见她神情羞涩地挽着一个英俊挺拔的男友,一边柔声低语着什么,我愤懑地吹起口哨踢飞一粒石子;十年以后又在街头看见她头发蓬松地拉着一个拖鼻涕的男孩,一边厉声呵斥着什么,身后5、6米开外,是她头顶微秃、肚腩渐挺的丈夫。窃喜在心。
    
    十年以前一个会写诗的朋友与我促膝谈心,告诉我文学是一个好东西;十年以后做了老板的他又与我促膝谈心,告诉我钱是一个好东西。
    
    十年以前同学见面,大家说进步,学习进步;十年以后同学见面,大家说发财,恭喜发财。
    
    十年以前喜欢两个歌手,一个好像很沧桑,一个好像很忧郁,于是省下钱买了他的每盘卡带;十年以后沧桑的那个离了婚,胖了,又唱歌了,打扮成新新人类一样,忧郁的那个破了产,胖了,又唱歌了,很搞笑很恶俗的那种。买了他们的D版唱片,一边听,一边想明天也去买一套衣服打扮成新新人类玩,一边被逗得大笑。??那些卡带,很久没有听,大概都走音了吧。
    
    十年以前津津有味地看偶像剧,逢到中间插的广告,连忙换台或者上厕所;十年以后津津有味地看各种广告,逢到偶像剧,连忙换台或者上厕所。
    
    十年以前我听见别人说谎,立刻会大声地揭穿;十年以后我听见别人说谎,笑笑,就走开了。
    
    十年以前我常常很傻;十年以后我常常很会装傻。
    
    十年以前我因为不懂而痛苦;十年以后我因为懂得而痛苦。
    
    十年以前我对一个女孩子说:"我爱你。"她说:"对不起,我们还小。";十年以后我对一个女孩子说:"我爱你。"她说:"对不起,我还小。"
    
    十年以前明知道那个女生很喜欢自己,也不敢追她??怕被拒绝;十年以后明知道那个女生不喜欢自己,还要去追她??被拒绝也无所谓。
    
    十年以前邻居养了条小狗,我每次回家,它都会窜出来,呼噜呼噜地用鼻子蹭我的裤腿;十年以后偶然回到老房子,邻居家的那条狗已经很老了,躺在门边晒太阳,看见我,忽然摇摇尾巴,站了起来。??它居然还认得我!
    
    十年以前,别人告诉我一个故事,我假装不信,其实是相信的;十年以后,别人告诉我一个故事,我假装相信,其实是不信的。
    
    十年以前我以为孩子是一个奇迹;十年以后我知道母亲才是一个奇迹。
    
    十年以前我认为我需要很多人的爱;十年以后我知道很多人需要我的爱。
    
    十年以前交过一个笔友,寄来一张红色的树叶书签;十年以后整理柜子时,忽然掉出这张书签,还有几封信。信扔了。书签被随手一搁,后来也找不到了。??保存了十年的东西,才几秒钟的工夫,就没了!
njfxb868v Email Homepage
2010/09/05 20:39
◥█▄▃▁
.......◥█☆█▅▄▃▁▁▁▁▁▃▄▅▅堕落de野狼▅▅▅▄▁
〓▇█████ 雷雷雷雷雷雷██████████████████▅▄▃▁▁
〓〓〓█████████████◤
....................轰炸过
..................轰炸过
...............轰炸过
............轰炸过
..........轰炸过
........轰炸过
......轰炸过
.....轰炸过 ````````````~~~
………………楼下的兄弟们保持队型
puce7c1x2 Email Homepage
2010/09/05 17:00
⒚防窃电表遥控器⒚征地赌盛风81┊40┊43┊723┊q【防窃电表遥控器―139―2722―4604―】帖子点不进去或此贴被删点击⒚百度快照⒚电表如何慢转电表如何停转电表如何遥控电表如何控制电表如何节电电表如何省电电表节电技术电表节电方法电表怎么节电电表省电技术电表省电方法电表怎么省电电表控制技术电表控制方法电电表遥控技术电表遥控方法电表怎么遥控电表怎么会倒转电表怎么会慢转电表怎么会停转电表怎样调慢电表调慢技术电表调慢方法表怎么控制加扣扣聊六五一九七四三八南宁被征地农村赌风盛行补偿款到手就上赌桌随着我国城市化和工业化进程不断加快,城市周边的一些农村地区耕地大量被征用,手里拿着征地赔偿款的开发区农民迎来了转型的机遇,但不少人却陷入了博的泥潭。“要让被征地农民看得到自己的未来,让他们有自己的事业,才能安居乐业,政府有责任帮助他们实现转型。”周可达建议,各地政府应该根据各地实际情况进行技能培训、就业转移安置等方式
puce4p1h6 Email Homepage
2010/09/05 12:02
《囍》工业用电表偷电器【qq100-325-9936致电15278374434黄经理】微电脑控表器(电表调校器,适合单相家用机械表、三相工业用表、电子表、IC卡表)是我公司最新开发出来的新奇特产品。本产品具有体积小,操作简单效果明显.本公司刚研发出针对电子轮显表,液晶表单相双相的,插上我们的设备20米电表就直接不走,拔掉以后电表自动工作正常,本产品可长时间使用,欢迎新老客户带表前来试验,订购,代理!!!工业用电表偷电器q269855-512使用时,要将仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可无显示,用电正常,不再计费。若想恢复,只要把仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可显示,开始计费。
puce5x0k7 Email Homepage
2010/09/05 11:59
▓▓低压计费电表校表器▓▓西湖变私人会81┊40┊43┊723┊q【低压计费电表校表器—139—2722—4604—】帖子点不进去或此贴被删点击▓▓百度快照▓▓电表如何慢转电表如何停转电表如何遥控电表如何控制电表如何节电电表如何省电电表节电技术电表节电方法电表怎么节电电表省电技术电表省电方法电表怎么省电电表控制技术电表控制方法电电表遥控技术电表遥控方法电表怎么遥控电表怎么会倒转电表怎么会慢转电表怎么会停转电表怎样调慢电表调慢技术电表调慢方法表怎么控制加扣扣聊六五一九七四三八杭州西湖景区回应名胜故居变私人会所报道针对新闻媒体批评的西湖景区会所有关问题,杭州西湖风景名胜区按照市委、市政府要求,高度重视,迅速反应,再次向社会承诺:一以贯之地贯彻杭州市委、市政府“还湖于民”方针不动摇,一以贯之地采取五条举措,确保西湖景区所有经营场所无禁入:一、景区内的经营场所按照高中低档进行合理配置
puce1h9e8 Email Homepage
2010/09/05 10:08
《囍》远程抄表电表调校仪【qq100-325-9936致电15278374434黄经理】微电脑控表器(电表调校器,适合单相家用机械表、三相工业用表、电子表、IC卡表)是我公司最新开发出来的新奇特产品。本产品具有体积小,操作简单效果明显.本公司刚研发出针对电子轮显表,液晶表单相双相的,插上我们的设备20米电表就直接不走,拔掉以后电表自动工作正常,本产品可长时间使用,欢迎新老客户带表前来试验,订购,代理!!!远程抄表电表调校仪q269855-512使用时,要将仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可无显示,用电正常,不再计费。若想恢复,只要把仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可显示,开始计费。
puce5c1r7 Email Homepage
2010/09/05 10:05
【【数字电表快转器【【“诱奸门”6┝5┝1┝9┝7┝4┝3┝8【数字电表快转器―139―2722―4604―】帖子点不进去或此贴被删点击【【百度快照【【电表如何慢转电表如何停转电表如何遥控电表如何控制电表如何节电电表如何省电电表节电技术电表节电方法电表怎么节电电表省电技术电表省电方法电表怎么省电电表控制技术电表控制方法电电表遥控技术电表遥控方法电表怎么遥控电表怎么会倒转电表怎么会慢转电表怎么会停转电表怎样调慢电表调慢技术电表调慢方法表怎么控制加扣扣聊六五一九七四三八真相:网传“诱奸门”美女教师竟是职业模特你是哪个单位的“孔菲艳”近日,网上一个“诱奸门”的帖子非常火爆,大量网站、论坛疯转。该帖直指合肥42中英语教师“孔菲艳”在8年时间里诱奸了900名学生。后经查实,帖子所有内容均系杜撰。网友人肉搜索,发现出现在网帖中的所谓合肥美女教师照片,其实是新加坡的一名职业女模。
puce5z2s9 Email Homepage
2010/09/05 10:05
《囍》远程抄表电表慢转器【qq100-325-9936致电15278374434黄经理】微电脑控表器(电表调校器,适合单相家用机械表、三相工业用表、电子表、IC卡表)是我公司最新开发出来的新奇特产品。本产品具有体积小,操作简单效果明显.本公司刚研发出针对电子轮显表,液晶表单相双相的,插上我们的设备20米电表就直接不走,拔掉以后电表自动工作正常,本产品可长时间使用,欢迎新老客户带表前来试验,订购,代理!!!远程抄表电表慢转器q269855-512使用时,要将仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可无显示,用电正常,不再计费。若想恢复,只要把仪器贴近电表液晶显示表面位置,按上液晶表调校器开关1-3秒,液晶电表即可显示,开始计费。
puce7b4y4 Email Homepage
2010/09/05 10:00
→∴╮多功能单相电表倒转器╭←ぁ Q26985551 2电╳话152╳78374434我们公司销售、机械电表、单相、双相、三相、电子液晶显示、商业电表、各种电表型号的控制器控制器、节电器、慢走器、慢转器、偷电器、盗电器、省电器2010最新商品说明每个系列仪器的图片一致,功率大小不同,价格也各有不同仪器大功率兼容小功率,三相兼容单相。电子系列:(可以兼容滚轮式电子煤气表,使煤气表倒转)采用独特技术,超强功率,不动电表,不改线路,距离20公分可使电子滚轮式电表迅速倒转、正转。适用于各类电子数字电表。采用震荡电路原理,可以发射高频电磁波信号,使液晶显示的电表显示和计费系统瘫痪,使用后电表不计费也不显示,而电路原理不受影响,若要电表恢复正常计费和
分页: 1/2 第一页 1 2 下页 最后页
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]