Java中文乱码问题产生原因分析_第1页
Java中文乱码问题产生原因分析_第2页
Java中文乱码问题产生原因分析_第3页
Java中文乱码问题产生原因分析_第4页
Java中文乱码问题产生原因分析_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Java中文乱码问题产生原因分析在计算机中,只有二进制的数据,不管数据是在内存中,还是在外部存储设备上。对于我们所看到的 字符,也是以二进制数据的形式存在的。不同字符对应二进制数的规则,就是字符的编码。字符编码的集 合称为字符集。17.1.1常用字符集在早期的计算机系统中,使用的字符非常少,这些字符包括26个英文字母、数字符号和一些常用符 号(包括控制符号),对这些字符进行编码,用1个字节就足够了(1个字节可以表示28=256种字符)。然 而实际上,表示这些字符,只使用了 1个字节的7位,这就是ASCII编码。ASCIIASCII (American Standard Code for Inf

2、ormation Interchange,美国信息互换标准代码),是基于常用的 英文字符的一套电脑编码系统。每一个ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的 十进制数是0127。例如,数字字符“0”的编码用十进制数表示就是48。另有128个扩展的ASCII码, 最高位都是1,由一些图形和画线符号组成。ASCII是现今最通用的单字节编码系统。ASCII用一个字节来表示字符,最多能够表示256种字符。随着计算机的普及,许多国家都将本地的 语言符号引入到计算机中,扩展了计算机中字符的范围,于是就出现了各种不同的字符集。ISO8859-1因为ASCII码中缺少、u和许多书写其他

3、语言所需的字符,为此,可以通过指定128以后的字符来 扩展ASCII码。国际标准组织(ISO)定义了几个不同的字符集,它们是在ASCII码基础上增加了其他语 言和地区需要的字符。其中最常用的是ISO8859-1,通常叫做Latin-1。Latin-1包括了书写所有西方欧洲语 言不可缺少的附加字符,其中0127的字符与ASCII码相同。ISO 8859另外定义了 14个适用于不同文字 的字符集(8859-2到8859-15)。这些字符集共享0127的ASCII码,只是每个字符集都包含了 128255 的其他字符。GB2312 和 GBKGB2312是中华人民共和国国家标准汉字信息交换用编码,全称

4、信息交换用汉字编码字符集一基本 集,标准号为GB2312-80,是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于 中国大陆和新加坡,简称国标码。因为中文字符数量较多,所以采用两个字节来表示一个字符,分别称为高位和低位。为了和ASCII码 有所区别,中文字符的每一个字节的最高位都用1来表示。GB2312字符集是几乎所有的中文系统和国际 化的软件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二级汉字和9区的 符号,其编码范围是高位0 xa1-0 xfe,低位也是0 xa1-0 xfe,汉字从0 xb0a1开始,结束于0 xf 7fe。为了对更多的字符和符号进

5、行编码,由前电子部科技质量司和国家技术监督局标准化司于1995年12 月颁布了 GBK(K是“扩展”的汉语拼音第一个字母)编码规范,在新的编码系统里,除了完全兼容GB2312 外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。它也是现阶段Windows和其他一些中文操 作系统的默认字符集,但并不是所有的国际化软件都支持该字符集。不过要注意的是GBK不是国家标准, 它只是规范。GBK字符集包含了 20 902个汉字,其编码范围是0 x8140-0 xfefe。每个国家(或区域)都规定了计算机信息交换用的字符编码集,这就造成了交流上的困难。想像一下, 你发送一封中文邮件给一位远在西班牙的朋友

6、,当邮件通过网络发送出去的时候,你所书写的中文字符会 按照本地的字符集GBK转换为二进制编码数据,然后发送出去。当你的朋友接收到邮件(二进制数据) 后,查看信件时,会按照他所用系统的字符集,将二进制编码数据解码为字符,然而由于两种字符集之间 编码的规则不同,导致转换出现乱码。这是因为,在不同的字符集之间,同样的数字可能对应了不同的符 号,也可能在另一种字符集中,该数字没有对应符号。为了解决上述问题,统一全世界的字符编码,由Unicode协会1制定并发布了 Unicode编码。UnicodeUnicode (统一的字符编码标准集)使用065535的双字节无符号数对每一个字符进行编码。它不仅 包含

7、来自英语和其他西欧国家字母表中的常见字母和符号,也包含来自古斯拉夫语、希腊语、希伯来语、 阿拉伯语和梵语的字母表。另外还包含汉语和日语的象形汉字和韩国的Hangul音节表。目前已经定义了 40000多个不同的Unicode字符,剩余25000个空缺留给将来扩展使用。其中大约201 Unicode协会是由IBM、微软、Adobe、SUN、加州大学伯克利分校等公司和组织所组成的非营利性组织。 000个字符用于汉字,另外11000左右的字符用于韩语音节。Unicode中0255的字符与ISO8859-1中的 一致。Unicode编码对于英文字符采取前面加“0”字节的策略实现等长兼容。如“a”的ASC

8、II码为0 x61, Unicode 码就为 0 x00, 0 x61。UTF-8使用Unicode编码,一个英文字符要占用两个字节,在Internet上,大多数的信息都是用英文来表示 的,如果都采用Unicode编码,将会使数据量增加一倍。为了减少存储和传输英文字符数据的数据量,可 以使用UTF-8编码。UTF-8 全称是 Eight-bit UCS Transformation Format (UCS, Universal Character Set,通用字符集,UCS 是所有其他字符集标准的一个超集)。对于常用的字符,即0127的ASCII字符,UTF-8用一个字节来表 示,这意味着只包

9、含7位ASCII字符的字符数据在ASCII和UTF-8两种编码方式下是一样的。如果字符对 应的Unicode码是0 x0000,或在0 x0080与0 x007f之间,对应的UTF-8编码是两个字节,如果字符对应的 Unicode码在0 x0800与0 xffff之间,对应的UTF-8编码是三个字节。因为中文字符的Unicode编码在0 x0800 与0 xffff之间,所以数据如果是中文,采用UTF-8编码数据量会增加50%。Unicode与UTF-8转换的规则简述如下:如果Unicode编码的16位二进制数的前9位是0,则UTF-8编码用1个字节来表示,这个字节的 首位是“0”,剩下的7位

10、与原二进制数据的后7位相同。例如:Unicode 编码:u0061 = 00000000 01100001UTF-8 编码:01100001 = 0 x61如果Unicode编码的16位二进制数的头5位是0,则UTF-8编码用2个字节来表示,首字节以“110” 开头,后面的5位与原二进制数据除去前5个零后的最高5位相同;第二个字节以“ 10”开头,后面的6 位与原二进制数据中的低6位相同。例如:Unicode 编码:u00A9 = 00000000 10101001UTF-8 编码:11000010 10101001 = 0 xC2 0 xA9如果不符合上述两个规则,则用三个字节表示。第一个字

11、节以“ 1110”开头,后四位为原二进制 数据的高四位;第二个字节以“10”开头,后六位为原二进制数据中间的六位;第三个字节以10”开头, 后六位为原二进制数据的低六位。例如:Unicode 编码:u4E2D = 01001110 00101101UTF-8 编码:11100100 10111000 10101101 = 0 xE4 0 xB8 0 xADI! 注意在UTF-8编码的多字节串中,第一个字节开头1”的数目就是整个字符串中字节的数目。17.1.2对乱码产生过程的分析为了让使用Java语言编写的程序能在各种语言的平台下运行,Java在其内部使用Unicode字符集来表 示字符,这样就

12、存在Unicode字符集和本地字符集进行转换的过程。当在Java中读取字符数据的时候,需 要将本地字符集编码的数据转换为Unicode编码,而在输出字符数据的时候,则需要将Jnicode编码转换为 本地字符集编码。例如,在中文系统下,从控制台读取一个字符“中”,实际上读取的是“中”的GBK编码0 xD6D0, 在Java语言中要将GBK编码转换为Unicode编码0 x4E2D,此时,在内存中,字符“中”对应的数值就是 0 x4E2D,当我们向控制台输出字符时,Java语言将Unicode编码再转换为GBK编码,输出到控制台,中 文系统再根据GBK字符集画出相应的字符。从上述过程来看,读取和写

13、入的过程是可逆的,那么理应不会出现中文乱码问题。然而,实际应用的 情形,比上述过程要复杂得多。在Web应用中,通常都包括了浏览器、Web服务器、Web应用程序和数 据库等部分,每一部分都有可能使用不同的字符集,从而导致字符数据在各种不同的字符集之间转换时, 出现乱码的问题。在Java语言中,不同字符集编码的转换,都是通过Unicode编码作为中介来完成的。例如,GBK编 码的字符“中”要转换为ISO-8859-1 (同ISO8859-1)编码,其过程如下:因为在Java中的字符,都是用Unicode来表示的,所以GBK编码的字符“中”要转换为Unicode 表示:0 xD6D0-0 x4E2D

14、。将字符“中”的Unicode编码转换为ISO-8859-1编码,因为Unicode编码0 x4E2D在ISO-8859-1 中没有对应的编码,于是得到0 x3f,也就是字符“?”。下面的代码演示了这一过程:/GBK编码的字符“中”转换为Unicode编码表示String str=中;/将字符“中”的Unicode编码转换为ISO-8859-1编码byte b=str.getBytes(ISO-8859-1);for(int i=0;ib.length;i+)(/输出转换后的二进制代码。System.out.print(bi);当从Unicode编码向某个字符集转换时,如果在该字符集中没有对应

15、的编码,则得到0 x3f (即问号字 符?)。这就是为什么有时候我们输入的是中文,在输出时却变成了问号。从其他字符集向Unicode编码转换时,如果这个二进制数在该字符集中没有标识任何的字符,则得到 的结果是0 xfffdo例如一个GBK的编码值0 x8140,从GB2312向Unicode转换,然而由于0 x8140不在 GB2312字符集的编码范围(0 xa1a1-0 xfefe),当然也就没有对应任何的字符,所以转换后会得到0 xfffd。 下面的代码演示了这一过程。/构造一个二进制数据。byte buf=(byte)0 x81,(byte)0 x40,(byte)0 xb0,(byte

16、)0 xa1;/将二进制数据按照GB2312向Unicode编码转换。String str=new String(buf,GB2312);for(int i=0;istr.length();i+)/取出字符串中的每个Unicode编码的字符。char ch=str.charAt(i);/务该字符对应的Unicode编码以十六进制的形式输出。System.out.print(Integer.toHexString(int)ch);System.out.print(-);/输出该字符。System.out.println(ch);在输出字符和字符串的时候,会从Unicode编码向中文系统默认的编码

17、GBK转换,由于Unicode编码 0 xfffd在GBK字符集中没有对应的编码,于是得到0 x3f,输出字符“?”。最后输出的结果如下:fffd-?40-554a-啊从上述所知,由于存在着多种不同的字符集,在各种字符集之间进行转换,就有可能出现乱码,同样 是中文字符集GB2312和GBK,由于编码范围的不同,某些字符在转换时也会出现乱码。在一个使用了数据库的Web应用程序中,乱码可能会在多个环节产生。由于浏览器会根据本地系统默 认的字符集来提交数据,而Web容器默认采用的是ISO-8859-1的编码方式解析POST数据,在浏览器提 交中文数据后,Web容器会按照ISO-8859-1字符集来解

18、码数据,在这一环节可能会导致乱码的产生。由于 大多数数据库的JDBC驱动程序默认采用ISO-8859-1的编码方式在Java程序和数据库之间传递数据,我 们的程序在向数据库中存储包含中文的数据时,JDBC驱动首先将程序内部的Unicode编码格式的数据转 化为ISO-8859-1的格式,然后传递到数据库中,在这一环节可能会导致乱码的产生。目前流行的关系型数 据库系统都支持数据库编码,也就是说在创建数据库时可以指定它自己的字符集设置,数据库的数据以指 定的编码形式存储。当JDBC驱动向数据库中保存数据时,有可能还会发生字符集的转换。正是由于在 Web应用程序运行过程中,输入的中文字符需要在不同的

19、字符集之间来回转换,也就导致了中文乱码问题 的频繁出现。图17-1描述了在Web应用的请求响应过程中,发生的字符编码转换过程,其中浏览器是正6.0,Web 容器的是 Tomcat 6.0.16。从图17-1描述的过程中可以看到,如果在Web应用程序中不指定任何的字符集,从浏览器端传来的 中文字符,输出回浏览器时,可以正常显示(以简体中文的方式查看网页)。然而,事情并没有这么简单, 在Servlet/JSP中,可能存在着直接写入的或从其他来源读取的中文字符,如果这些字符对应的Unicode码 是从GB2312编码转换而来,那么以ISO-8859-1编码方式输出,这些字符将不能正常显示。所以对于中

20、文 的处理,应该在图17-1和的位置明确指定使用GB2312或GBK字符集。图17-1在Web请求响应过程中,中文字符编码的转换过程中文乱码问题的解决方案只要掌握了中文乱码问题产生的原因,然后对症下药,就可以顺利地解决这些问题。下面我们对容易 产生乱码问题的场景进行分析,并提出解决方案。以POST方法提交的表单数据中有中文字符由于Web容器默认的编码方式是ISO-8859-1,在Servlet/JSP程序中,通过请求对象的getParameter() 方法得到的字符串是以ISO-8859-1转换而来,这是导致乱码产生的原因之一。为了避免容器以ISO-8859-1 的编码方式返回字符串,对于以

21、POST方法提交的表单数据,可以在获取请求参数值之前,调用 request.setCharacterEncoding(GBK”),明确指定请求正文使用的字符编码方式是GBK。在向浏览器发送 中文数据之前,调用response.setContentType(text/html;charset=GBK”),指定输出内容的编码方式是GBK。对于JSP页面,在获取请求参数值之前,写上下面的代码:为了指定输出内容的编码格式,设置page指令contentType属性,如下:在Web容器转换JSP页面后的Servlet类中,会自动添加下面的代码:response.setContentType(text/h

22、tml; charset=GBK);以GET方法提交的表单数据中有中文字符当提交表单采用GET方法时,提交的数据作为查询字符串被附加到URL的末端,发送到服务器,此 时在服务器端调用setCharacterEncoding()方法也就没有作用了。我们需要在得到请求参数的值后,自己做 正确的编码转换。String name = request.getParameter(name);name=new String(name.getBytes(ISO-8859-1),GBK);在第一行,调用getParameter()方法得到的字符串name的Unicode值是以ISO-8859-1编码转换而来,

23、调用name.getBytes(ISO-8859-1),将得到原始的GBK编码值,接着,对new String()的调用将以GBK 字符集重新构造字符串的Unicode编码。为了方便从ISO-8859-1编码到GBK的转换,我们可以编写一个工具方法,如下:public String toGBK(String str)throws java.io.UnsupportedEncodingException(return new String(str.getBytes(ISO-8859-1),GBK);在数据库中存储和读取中文数据对于大多数数据库的JDBC驱动程序,在Java程序和数据库之间传递数据都是以ISO-8859-1为默认 编码格式,所以,我们在程序中向数据库存储包含中文的数据时,JDBC驱动程序首先把程序内部的Unicode 编码格式的数据转化

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论