|
[中文] [English]
|
|
|
中文编码及JAVA中文乱码问题解疑[从兴公司 - 陈百平] 做过JSP开发的同事可能都有着这样的经历:当开发完页面后,使用浏览器一看,整个页面是乱码,一番设置后,发现页面静态部分好了,但JSP的中文字符串输出还是乱码。再经过一番调整,JSP的中文字符串终于正常显示了,以为可以松一口气了,但随后又发现数据库的中文数据显示还是乱码! 要了解引起乱码的原因,还得先分清以下的编码(或称字符集character set)概念及其之间的相互关系。 1.区位码:我国国家标准汉字信息交换用编码,全称《信息交换用汉字编码字符集基本集》,标准号为 GB 2312-80(GB 是“国标”二字的汉语拼音缩写),由中华人民共和国国家标准总局发布,1981年5月1日实施,新加坡等地也使用这一编码。区位码是一个四位的十进制数,它的前两位叫做区码,后两位叫做位码。区位码的编码范围是:0101~9494。比如,按拼音排序后的第一个汉字是“啊”,它的区位码为1601(十六进制为1001 H)。下图是新华字典所显示的汉字“啊”的信息。
2.GB2312 码:又称为国标码,在区位码的基础上,分别将区号值和位号值各加 32(20H)而得到,于是它的编码范围是2121H~7E7EH,于是“啊”的GB2312 的编码为3021 H(即1001H+2020H)。但是,因为GB2312 与ASCII有重叠,通行方法是将GB2312 码两个字节的最高位置1以示区别,编码范围变为 0A1A1H~0FEFEH(实际只用到0xF7FEH)。这种在第八位置"1",提示计算机转入双字节的编码方式也叫EUC(Extended UNIX Code)编码,所以“啊”的GB2312 的最终编码为B0A1 H(即3021 H+8080H)。其实GB2312 和区位码是一样的,应该合在一起谈。但事实上两个的编码又不一样,我也不知为什么要弄得如此复杂。或许可以这样理解:区位码是一个编码方案,而 GB2312 编码则是这个方案在计算机里的一个具体实现。GB2312 将收录的汉字分成两级:第一级是常用汉字计3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于 56-87区,按部首/笔画顺序排列。共计6763个汉字。 3.GBK码:它是GB2312 的一次大的扩展,编码向下兼容GB2312 的EUC编码。GBK包含了20902个汉字,其编码范围是8140-FEFE H。因此GBK包含了BIG5,Shift-JIS,KSC(中文繁体、日文和韩文)的字汇。注意只是包含字汇,但编码值还是不同的。在具体应用中,用 GBK字体就可以同时显示GB2312 ,BIG5,Shift-JIS,KSC的字符串。但除了GB2312 字符串,其它都要转换。当年朱镕基当上总理时,GBK还不是很普及,以至各家网络媒体报导新闻时,“镕”字经常使用图片来替代,或者使用两个汉字“钅容” 组成。可能是这件事情后,GBK就比较快地普及了。 4.GB18030-2000 码:在GBK的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的字形。GB18030收录了27484个汉字,总编码空间超过150万个码位。 GB18030编码是变长的,其二字节部分与GBK兼容;四字节部分是扩充的字形、字位。GB18030是国家强制性标准,而GBK不是。 5.BIG5码:我们宝岛台湾与香港特别行政区的常用编码是BIG5码。对于同一个汉字,BIG5码的码值与GB18030是不一样的。如“啊”的BIG5码为B0DA H,并且汉字的排序方式是按笔划排序的。 6.ISO8859-1:说完中文,提一下英文的编码方式。最常用的是ASCII编码,编码范围是00 H-7F H。ISO8859编码是对ASCII的扩展,扩展部分是80 H-FF H,用于定义其它非英文字母,如法语、德语等。其中ISO8859-1是最著名的一个,定义了西欧拉丁语符号。注意,ISO8859-1与GB18030 在编码上也是重叠的。 7.代码页:对于一个具体编码值,不同的编码方式对应着不同的汉字。例如,编码“B0A1 H”在GBK中为汉字“啊”,在BIG5中则是汉字“陛”,而在西欧ISO8859-1编码方式中则是两个字符‘°’和‘¡’。操作系统如何确知它到底代表的是GBK、BIG5还是其它的字符呢?Windows使用代码页(code page)来解决这个问题。代码页所存储的是编码与计算机内码的对应关系。Window通过查看当前用户设置的代码页来显示正确中文的汉字。例如中文 Window2000的缺省代码页是CP936,也即BGK所对应的代码页。Window的代码页可以通过控制面板的“区域选项”来设置成其他的代码页。 UNIX下则可以通过设置环境变量LC_ALL或LANG来设置当前的语言区域代码。 8.UNICODE:随着国际互联网的迅速发展,要求进行数据交换的需求越来越大,不同的编码体系越来越成为信息交换的障碍,而且多种语言共存的文档不断增多,单靠代码页已很难解决这些问题,于是UNICODE应运而生。 UNICODE 通常用作涉及双字节字符编码方案的通用术语。UNICODE CCS 3.1(CCS:编码字符集,coded character set) 的官方称谓是 ISO10646-1 通用多八字节编码字符集(Universal Multiple Octet Coded Character Set,UCS)。 UNICODE 编码系统可分为编码方式和实现方式两个层次。 目前用于实用的 UNICODE 版本的编码方式使用16位的编码空间,也就是每个字符占用2个字节。这样理论上一共最多可以表示 65536 个字符,基本满足各种语言的使用。标准的UNICODE编码方案使用32位的的编码空间,目前编码范围是0到10FFFF H。例如“啊”的UNICODE编码为55 4A H。 UNICODE 的实现方式不同于编码方式。一个字符的 UNICODE 编码是确定的。但是在实际传输过程中,由于不同系统平台对非ASCII码的处理都不太一致,以及出于节省空间的目的,对 UNICODE 编码的实现有着不同的方式。UNICODE 的实现方式称为UNICODE转换格式(UNICODE Translation Format,简称为 UTF)。常用的有: 1)UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-6 byte。其与UNICODE编码的关系如下: U-00000000-U-0000007F: 0xxxxxxx 2)UTF-16,16位编码,基本上就是UNICODE编码的实现。这里只能说是“基本上”,因为对于码值在10000H到10FFFFH之间的 UNICODE码,UTF-16是使用“代理对”(即两个UNICODE码)来实现编码的。幸好,我们常用的字符,包括各国的文字、符号等,都可以使用单个UTF-16来编码,不需要通过代理对实现。因此,UTF-16是UNICODE编码实现的指定首选编码方式。例如“啊”的UTF-16编码是55 4A H,占2字节,并且与实际“啊”的实用16位UNICODE编码值是相等的。 3)UTF-32,32位编码,顾名思义,与标准UNICODE编码是一一对应的。例如“啊”的UTF-32编码是00 00 55 4A,占4字节。 上面罗列了一堆编码,希望大家还没有看晕过去。我们现在分析几种JAVA中的乱码问题。 首先,我们看看Window是如何处理文本文件的。Window2000内核是使用UNICODE编码进行字符处理的。Window上的纯文本编辑器(包括 JBuilder等JAVA开发工具的编辑器),在读进一个纯文本文件时,如果该文件本身没有指定编码格式,则编辑器会使用当前Window的代码页(中文Window下是CP936,即GBK)对文件内容字节流进行转换,转换成UNICODE编码。在保存时,则将内存中的UNICODE编码通过代码页转换为本地编码,以字节流形式存入磁盘。 JAVA核心也使用UNICODE进行字符处理,与Window2000是一致的,因此,处理文件的过程也和上面是一样的。因为internet传输也是以字节流方式传输的,因此JAVA在处理网络字节流时也按上述方式进行。在这种byte->char->byte类型的转换中,如果中间出现编码转换错误,就会冒出中文乱码问题了。 案例一:JAVA产生的文件(或控制台输出),在Window下正常,上传到UNIX运行时乱码。这是因为两台机器的代码页不一致造成的。解决办法:将UNIX上的语言区域设为与Window的一致后,再运行JAVA程序。 案例二:文本文件使用Window文本编辑器可以正常打开,而JAVA应用程序读入该文件时乱码。原因是文本文件有多种的编码方式包括ASCII,UTF- 8,UTF-16(而CPU有着字节顺序的问题,故UTF-16又细分为UTF-16LE小尾序、UTF-16BE大尾序两种编码)。文本文件在缺省下是根据当前系统的区域选定的编码方式来保存的,Window下也就是代码页。解决办法:检查该文本文件的编码方式,并在JAVA程序中按该编码方式读取数据。 如果是互连网应用,则出现的乱码情况会更多。 案例三:某个JSP页面已指定了编码为GB2312 <%@ page contentType="text/html; charset= GB2312"%>,并且html代码中也指定了编码为,但页面还是有乱码,而其它的页面却是正常的,编码设置也是一样。原因是JSP使用了非 GB2312 编码字符,页面不知如何显示该字符。解决办法:改用GBK或GB18030编码。 案例四:JSP页面或servlet完成后,运行一直都是乱码。解决方法:请检查当前系统区域设置、JSP中的page contentType设置、servlet中的response.setContentType和html代码中的meta http-equiv="Content-Language"是否都设置了相同的编码方式。如果使用集成开发工具如JBuilder等,还需要检查集成环境中开发项目所使用的编码是否与上述的编码一致。 案例五:JSP页面已正常显示,但数据库记录数据显示为乱码。这还是由于数据库编码方式与JSP的编码方式不一致引起的。解决方法有3种,依次为: 1)如果数据库jdbc驱动支持编码转换,则在获取jdbc连接时,加上相应的编码信息便可。本方法最简单。 希望以上资料与分析能给大家带来帮助。 参考资料:
注:以上文章版权归立信集团《沟通》所有,不得转载、引用。 |