转自:http://www.52testing.com/showart.asp?id=59
一 概述
在我们测试Web应用项目的时,经常会遇到中文乱码问题,我也是在做框架测试时遇到这类情况,为什么会有中文乱码呢?带着这个疑问,对中文乱码问题产生的由来做了大致的分析,希望对各位有用。
二 字符集介绍
我们知道在计算机中,只有二进制的数据,不管数据是在内存中,还是在外部存储设备上;对于我们所看到的所有字符,都是以二进制数据的形式存在的,只不过字符对应二进制数的规则不同,这个规则就是字符的编码,我们常见的字符编码有:
1 ASCII字码
ASCII ( American Standard Code for Information Interchange,美国信息互换标准代码),是基于常用的英文字符的一套编码系统,每一个ASCII码与一个8位(bit )二进制数对应,其最高位是0,相应的十进制数是0-127。另有128个扩展的ASCII码,最高位都是1,由一些图形和画线符号组成。ASCII是现今最通用的单字节编码系统。
ASCII用一个字节来表示字符,最多能够表示256种字符。随着计算机的普及,许多国家都将本地的语言符号引入到计算机中,扩展了计算机中字符的范围,于是就出现了各种不同的字符集。
2 IS0-8859-1
通过指定128以后的字符来扩展ASCII码,国际标准组织(ISO)定义了几个不同的字符集,它们是在ASCII码基础上增加了其他语言和地区需要的字符。其中最常用的是IS0-8859-1,通常叫做Latin-1 , Latin-1包括了书写所有西方欧洲语言不可缺少的附加字符,其中0-127的字符与ASCII码相同,ISO8859另外定义了10个适用于不同文字的字符集(8859-2到8859-10和8859-15),这些字符集共享0-127的ASCII码,只是每个字符集都包含了128-255的其他字符。
3 GB2312和GBK
GB2312是咱们国家标准汉字信息交换用编码,全称《信息交换用汉字编码字符集-基本集》,标准号为GB2312-80,是一个由国家标准总局发布的关于简化汉字的编码,通行于中国大陆和新加坡,简称国标码。
因为中文字符数量较多,所以采用两个字节来表示一个字符,分别称为高位和低位。为了和ASCII码有所区别,中文字符的每一个字节的最高位都用1来表示。GB2312字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二级汉字和9区的符号,汉字从Oxb0a1开始,结束于Oxf7fe。
为了对更多的字符和符号进行编码,由前电子部科技质量司和国家技术监督局标准化司于1995年12月颁布了GBK 编码规范,在新的编码系统里,除了完全兼容GB2312外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。它也是现阶段Windows和其他一些中文操作系统的默认字符集,但并不是所有的国际化软件都支持该字符集,GBK字符集包含了20902个汉字,其编码范围是0x8140-Oxfefe。
每个国家都规定了计算机信息交换用的字符编码集,这就造成了交流上的困难。假想一下,你发送一封中文邮件给一个美国人,当邮件通过网络发送出去的时候,你所书写的中文字符会按照本地的字符集GBK转换为二进制编码数据,然后发送出去。那位美国朋友接收到邮件(二进制数据)后,查看信件时,会按照他所用系统的字符集,将二进制编码数据解码为字符,然而由于两种字符集之间编码的规则不同,导致转换出现乱码。这是因为,在不同的字符集之间,同样的数字可能对应了不同的符号,也可能在另一种字符集中,该数字没有对应符号。
为了解决上述问题,统一全世界的字符编码,由Unicode协会制定并发布了Unicode编码。
4 Unicode编码
Unicode(统一的字符编码标准集)使用0-65535的双字节无符号数对每一个字符进行编码,它不仅包含来自英语和其他西欧国家字母表中的常见字母和符号,还包含汉语和日语的象形汉字和韩国的音节表。
目前己经定义了40000多个不同的Unicode字符,剩余25000个空缺留给将来扩展使用。其中大约20000个字符用于汉字,另外11000左右的字符用于韩语音节。Unicode中0-255的字符与IS08859-1中的一致。
5 UTF-8
使用Unicode编码,一个英文字符要占用两个字节,在Internet上,大多数的信息都是用英文来表示的,如果都采用Unicode编码,将会使数据量增加一倍。为了减少存储和传输英文字符数据的数据量,可以使用UTF-8编码。
UTF-8对于常用的字符,即0-127的ASCII字符,UTF-8用一个字节来表示,这意味着只包含7位ASCII字符的字符数据在ASCII和UTF-8两种编码方式下是一样的。如果字符对应的Unicade码是0x0000,或在0x0080与Ox007f之间,对应的UTF-8编码是两个字节,如果字符对应的Unicode码在0x0800与oxfff之间,对应的UTF-8编码是三个字节。因为中文字符的Unicode编码在0x0800与oxfff之间,所以数据如果是中文,采用UTF-8编码数据量会增加50%。
三 对乱码差生的原因分析
通过以上介绍,我们知道世界上存在很多个字符集,每个国家都有自己的编码系统,Java为了使编写的程序能在各种语言的平台下运行,在其内部使用Unicode字符集来表示字符,这样就存在Unicode字符集和本地字符集进行转换的过程。当在Java中读取字符数据的时候,需要将本地字符集编码的数据转换为Unicode编码,而在输出字符数据的时候,则需要将Unicode编码转换为本地字符集编码。例如,在中文系统下,从控制台读取字符“中”,实际上读取的是“中”的GBK编码OxD6D0.在Java语言中要将GBK编码转换为Unicode编码Ox4E2D,此时,在内存中,字符“中”对应的数值就是Ox4E2D,当我们向控制台输出字符时,Java语言将Unicode编码再转换为GBK编码,输出到控制台,中文系统再根据GBK字符集画出相应的字符。在Java编码的字符语言中,不同字符集编码的转换,都是通过Unicode编码作为中介来完成的。
从上述过程来看,读取和写入的过程是可逆的,那么按说不会出现中文乱码问题。然而,实际应用的情形,比上述过程要复杂得多。在Web应用中,通常都包括了浏览器、Web服务器、Web应用程序和数据库等部分,每一部分都有可能使用不同的字符集,从而导致字符数据在各种不同的字符集之间转换时,出现乱码的问题,同样是中文字符集GB2312和GBK,由于编码范围的不同,某些字符在转换时也会出现乱码。
在这里我们举一个例子,来说明产生乱码的原因,例如,GBK编码下的’中”要转换为ISO-88591编码,其过程如下:
(1)因为在Java中的字符,都是用Unicode来表示的,所以GBK编码的字符“中”要转换为Unicode表示,即 :OxD6D0一>Ox4E2D。
(2)将字符“中”的Unicode编码转换为IS0-8859-1编码,因为Unicode编码Ox4E2D在ISO-8859-1中没有对应的编码,于是得到ox3f,也就是字符“?”,这就是我们经常看到中文变“?”的一个原因。
由于浏览器会根据本地系统默认的字符集提交数据,而Web容器默认采用的是IS0-8859-1的编码方式解析POST数据,另外JDBC驱动程序多数也采用ISO-8859-1的编码格式,因此,在Web应用程序运行过程中,输入的中文字符往往需要在不同的字符集之间来回转换,这也就一导致了中文乱码问题。
对于中文乱码问题的解决,可谓条条大道通罗马,在这里就不介绍了。