中文乱码问题分析Java_第1页
中文乱码问题分析Java_第2页
中文乱码问题分析Java_第3页
中文乱码问题分析Java_第4页
中文乱码问题分析Java_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

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

文档简介

1、第一章 基础知识1.1 介绍1.1.1 iso8859-1 属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母a的编码为0x61=97。ISO 8859-1 ,即俗称的latin-1,西欧字母。其中前半段是ASCII码。很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用iso8859-1编码来表示。而且在很多协议上,默认使用该编码。比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为例,应该是"d6d0 cec4"

2、两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:"d6 d0 ce c4"(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4 b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。1.1.2 GB系列 GB系列这就是汉子的国标码,专门用来表示汉字,是双字节编码,而英文字母和iso8859-1一致(兼容iso8859-1编码)。其中gbk编码能够用来同时表示繁体字和简体字,而gb2312只能表示简体字,gbk是兼容gb2312编码的。 GB2312-80 是在

3、国内计算机汉字信息技术发展初始阶段制定的,其中包含了大部分常用的一、二级汉字,和 9 区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,这也是最基本的中文字符集。其编码范围是高位0xa10xfe,低位也是 0xa1-0xfe;汉字从 0xb0a1 开始,结束于 0xf7fe;GBK 是 GB2312-80 的扩展,是向上兼容的。它包含了 20902 个汉字,其编码范围是 0x8140-0xfefe,剔除高位 0x80 的字位。其所有字

4、符都可以一对一映射到 Unicode 2.0,也就是说 JAVA 实际上提供了 GBK 字符集的支持。这是现阶段 Windows 和其它一些中文操作系统的缺省字符集,但并不是所有的国际化软件都支持该字符集,感觉是他们并不完全知道 GBK 是怎么回事。值得注意的是它不是国家标准,而只是规范。随着 GB18030-2000国标的发布,它将在不久的将来完成它的历史使命。GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字,增加了藏、蒙等少

5、数民族的字形。GBK2K 从根本上解决了字位不够,字形不足的问题。它有几个特点:1、 它并没有确定所有的字形,只是规定了编码范围,留待以后扩充。 2、 编码是变长的,其二字节部分与 GBK 兼容;四字节部分是扩充的字形、字位,其编码范围是首字节 0x81-0xfe、二字节0x30-0x39、三字节 0x81-0xfe、四字节0x30-0x39。3、 它的推广是分阶段的,首先要求实现的是能够完全映射到 Unicode 3.0 标准的所有字形。 4、 它是国家标准,是强制性的。 5、 现在

6、还没有任何一个操作系统或软件实现了 GBK2K 的支持,这是现阶段和将来汉化的工作内容1.1.3 unicode 这是最统一的编码,可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母a为"00 61"。需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处

7、理的,比如java。 ISO 10646, 即通用字符集(Universal Character Set, UCS),四个字节编码UCS 标准 (ISO 10646) 描述了一个 31 位字符集的体系, 不过, 目前只使用了前面 65534个编码位置 (0x0000-0xfffd, 它们被称为 基本多语言块 (Basic Multilingual Plane,BMP), 分配给了字符, 而且我们估计只有那些很古怪的字符(比如Hieroglyphics)为了专门的科学目的, 才会在将来的某个时候, 需要 16 位的 BMP 之外的部分. Unicode编码头256个字符和ISO8559-1一样。

8、前面补上空字节。UniHAN主要分布在U3400到U9FFF之间,此外,UF900到UFAFF之间也有一些。事实上,GB2312和BIG5的字符都分布在U4E00和U9FFF之间。Unicode中中文:4e00-9fa51.1.4 UTF 考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。另外,utf编码自带简单的校验

9、功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。 注意,虽然说utf是为了使用更少的空间而使用的,但那只是相对于unicode编码来说,如果已经知道是汉字,则使用GB2312/GBK无疑是最节省的。不过另一方面,值得说明的是,虽然utf编码对汉字使用3个字节,但即使对于汉字网页,utf编码也会比unicode编码节省,因为网页中包含了很多的英文字符。上图是UTF-8中所有三种内码格式,绿格中的值是固定的,兰格中是用来存放真正的内码。1.2 应用系统开发Web系统是遇到字符集问题最多的地方,也非常具有代表性,我们可以发现但凡遇到要进行跨进程间数据交换的时候,都可能会存在字符集的转

10、换问题,这就导致了我们需要对他进行分析。一个标准的web系统模型如下:这个时候我们就可以看到中间出现了三种的跨进程数据交换。都可能导致通讯不畅,在这个部分的讨论中,我们集中于这三种交换的讨论。1.2.1 与浏览器的交换l 控制浏览器浏览器提交的数据,包括url和http协议头部中的内容(包括post上来的数据,cookie等),都会存在字符集的问题,我们在服务器端能影响一部分,但不是全部。Ø 如何影响页面展现和Post方法提交时自动进行的数据编码。在web系统开发的时候,可以通过response.setContentType()方式直接告诉浏览器。或者通过html中嵌入meta的方式

11、告诉浏览器。<meta http-equiv="Content-Type" content="text/html;charset=utf-8">但是要注意, 这个 meta 标签必须放在 head 中靠前面的位置才能保证不会出问题。 因为 Web 服务器读到这里的时候,就会停止解析,然后用读到的这个编码方式重新解析页面。Ø 浏览器的url编码受本地字符集(一般中文windows为GBK)和浏览器设置(ie6中有个设置是总用utf-8发送url的选项)注意:这个设置对参数无效,只对URL的前半部分有效,也就是说,url=”中文一.js

12、p?para1=中文二”,这样的一个url,在发送的时候,只有【中文一】会被用utf-8编码,而【中文二】依然会使用本地字符集进行编码,对于中文操作系统来说,也就是GBK。Ø Cookie这个部分在存储的时候就已经是编好码的,浏览器在发送的时候并不参与编码的过程,所以这个部分是完全可控的。l 解析浏览器提交的数据对于浏览器提交的数据,在网络上以字节流的方式发送到服务器端,服务器在进行解析的时候必须知道提交的字符集是什么。一般的编程环境都提供对字节流的解析方式的设置,如java提供request.setCharacterEncoding方式。当然在早期的环境很多没有很好的国际化支持,而

13、且,各种web服务器的实现也影响解析的过程,在实战中需要具体问题具体分析。1.2.2 与数据库的交换数据库在构建时已经设置好了字符集,不会受应用程序的影响,无论读写操作,都需要尊重数据库的设置。我们能做的,只是在开发时,告诉应用程序,数据库的字符集是什么以便应用程序能正确识别数据。现在的数据库,一般可以在driver的连接上设置字符集,由封装好的driver包来控制字符的编码与解码。比如mysql的jdbc连接串:mysql.url=jdbc:mysql:/localhost/acai?useUnicode=true&characterEncoding =8859_11.2.

14、3 与文件的交换与文件的交换,为读写两个部分。l 读文件读文件时,必须告诉应用程序,将要读取的文件的字符集编码,从逻辑上来说这是很清晰的,但在实战中有问题出现了:Ø 为了编程方便,大多开发环境都支持default字符集,也就是在开发时无需到处指定,这时由于运行环境的变化,就会导致莫名的错误。Ø 文件所采用的字符集很难识别,由于各种便利性工具的出现,使得我们日常编写文件时越来越少的关注文件的字符集。导致经常性的错误出现。l 写文件Ø 写文件相对简单,将输出流指定字符集,如果没有指定,就会按照默认的字符集进行处理。1.2.4 总结由于java或者其他高级语言本身会做一

15、次从字节流到字符串的转换,将字节流以Unicode方式存储,所以编码问题变得重要起来。逻辑上,应用程序读到字节流然后按照你设定的字符集进行识别,并在内存中将此种识别结果自动存为Unicode编码方式,应用程序并不管是什么内容,但一定知道这个字符的Unicode的码值。从逻辑上来说,我们只要在字符转换动作发生之前先为程序设定字符集就可以了。然而上面的讨论都只是理想中的状况,问题是就只在程序这端我们依然不能获得完全的控制权。第二章 具体开发场景前面提及的内容都是理想情况下的,在实战中,由于各种环境实现的差异(如web容器,JDK版本,操作系统环境),实际遇到问题时还是需要具体情况具体分析。2.1

16、Java开发对于Java来说,所有的字符串,在内存中总是以UniCode存储的,注意是Unicode而不是utf-8。2.1.1 字符串的处理l getBytes(charset) 这是java字符串处理的一个标准函数,其作用是将字符串所表示的字符按照charset编码,并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文",正常情况下(即没有错误的时候)存储为"4e2d 6587",如果charset为"gbk",则被编码为"d6d0 cec4",然后返回字节"d6 d

17、0 ce c4"。如果charset为"utf8"则最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",则由于无法编码,最后返回 "3f 3f"(两个问号)。l new String(charset) 这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。参考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的结果"4e2d 65

18、87",但iso8859-1最后变成了"003f 003f"(两个问号)。 因为utf8可以用来表示/编码所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) 等于 str,即完全可逆。l 其他处理java.io包中很多关于“字符串输入输出”的类,包括Reader,InputStream,OutputStream等,都有相应的接口可以设置字符集。2.1.2 Web容器Java的web端需要用到web容器,我们很多的数据都是从Web容器中来的,最常用的有:1、 request.

19、getParameter()系列。2、 request.getRequestURI()3、 request.getServletPath()4、 request.getHeader()5、 request.getCookies()6、 。在我们拿到这些数据的时候已经是字符串了,如果编码出问题,就已经出了,所以我们要提前干预。1、setCharacterEncoding在servlet2.3规范中,专门为request对象增加了一个setCharacterEncoding()方法,在执行任何获取parameter相关的操作之前,设置好,就可以自定义对参数解码的字符集,同时也是在2.3规范中支持了

20、Filter属性,以便于能在一个地方进行控制。2.1.3 程序编译Java的class文件和内存中都是以Unicode形态存在,但是java源文件,jsp源文件却不一定,这个时候也可能出现编码问题。所以如果出现默认编译字符集和文件字符集不相同的情况,就需要在编译时加上-encoding选项。对于java文件,直接在javac后增加参数-encoding就行,对于在IDE中开发编译的情况,一般可以不用关心。对于JSP编译,有两种方式解决这种字符集差异:l 指定文件的存储编码。很明显,该设置应该置于文件的开头。例如:<%page pageEncoding="GBK"%&g

21、t;。l 经过测试,tomcat对jsp的编译除了受pageEncoding参数影响,还会受content-type影响。pageEncoding的优先级大于contenttype。构建一个最简单的jsp如下,注意,此时中文在实际存储时对应的字节码应该为D6D0CEC4。<% page language="java" pageEncoding="GBK"%><% page contentType="text/html; charset=GBK"%>中文我们用ultraEdit去修改上述文件测试,再结合tomca

22、t编程生成的java文件对比,可以得出上述结论。l 对于一些web容器可以设置相应的参数,来指定jsp源文件使用的字符集,如websphere6中对源文件字符集的设置为:-DFILE encoding = GBK2.1.4 JDK版本差异JDK的不同版本在处理字符时也有差异。tryString hex = "D632"String str = new String(hex2byte(hex),"GBK");System.out.println(str);catch(Exception ex)ex.printStackTrace();上述代码的功能是:将十

23、六进制表示为“D632”的字节流按照GBK编码转换为一个字符串,hex2byte方法是将16进制描述的字符串转换为对应的byte数组。在JDK1.5及以前版本和JDK1.6版本中反映的结果不一致,“D632”这个16进制的双字节字符在GBK里并没有对应的字符。环境显示JDK1.5?2JDK1.6?中文windows?Oracle?这个小差异,是在一次异构数据库迁移的时候发现的,当时我们有效利用了JDK1.5的这种特殊性,为数据迁移做了一个工具,用于识别迁移数据中出现的中文被截断的问题。2.1.5 JAVA 的encodingjava支持的encoding中与中文编程相关的有:(有几个

24、在JDK文档中未列出)1、 ASCII 7-bit, 同 ascii72、 ISO8859-1 8-bit, 同 8859_1,ISO-8859-1,ISO_8859-1,latin1. 3、 GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381,1383, Cp1383, ISO2022CN,ISO2022CN_GB. 4、 GBK (注意大小写),同MS936 5、 UTF8 UTF-8 6、 GB18030 , 同Cp1392,1392 2.2 tomcat设置2.2.1 url中的中文URL中包含中文(或者是get方法提交的数据中有中文),这个问题总是比较麻烦,需要服务器端和客户端保持一致。 一般情况下,在tomcat的server.

温馨提示

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

评论

0/150

提交评论