jsp 编码问题总结
下面引用网上一些帖子
JSP页面编码问题研究(转载)
<%@page contentType="text/html; charset=UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
中国
</body>
</html>
这个页面在为什么在运行的时候“中国”会变成乱码?
Analysis
Key Step
对于上面问题的分析需要从整个JSP页面请求的生命周期来看,一般的都需要经历下面几个阶段:
1。应用服务器根据JSP页面生成一个Java文件
2。应用服务器调用java.exe将Java文件编译成一个Servlet对应的class文件
3。用户的浏览器请求JSP对应的Servlet,Web容器起一个线程执行Servlet,将数据返回给客户端浏览器
4。用户的IE根据返回的数据,将结果显示给用户。
Key Step Analysis
为了更好的了解编码问题,我们暂时先从上面的四个环节一步步来分析,根据分析的结果,来得到最终的解决办法。
1. 在应用服务器根据JSP页面生成Java文件阶段。
应用服务器会将整个JSP页面的代码读取出来,然后写到一个新的JAVA文件
中,在读文件和写文件的时候都牵涉到一个编码问题,这个编码问题应用服务器是如何解决的呢?我研究Tomcat应用服务器的源代码,发现Tomcat中有
一个pageEncoding参数非常重要,在ParserController会从JSP文件中读出这个参数(如果没有读到,就从第一行的
contentType中读取charset),然后保存起来,如果没有读取到这个参数,会从JspConfig中读出一个默认的
PageEncoding参数,如果这两个参数都没有的设置,系统会默认成ISO8859-1的编码来读取原来的JSP文件。
从上面的分析出,我们已经基本了解了应用服务器读取JSP文件的编码方式,由于Java底层都是基于Unicode编码来存储字符的,所以在写文件的时候,都输出成Unicode编码的形式。
2。在JDK将Java文件编译成Class文件的时候
可以利用-encoding参数指定源文件的编码,这在手动编译的时候非常重
要,因为这决定了Java虚拟机读取Java文件时采用的编码方式,但是在Web应用中这个环节我们可以忽略,因为应用服务器可以很好的解决这个编码。以
Tomcat为例,由于生成的java文件是固定的UTF-8编码,所以Tomcat也固定的采用UTF-8编码来读取,通过浏览
AbstractCatalinaTask可以看到reader = new
InputStreamReader(hconn.getInputStream(),
CHARSET);其中的CHARSET=utf-8。所以在这个环节中应用服务器都可以很好的把握,不会带来编码问题。
3. 用户的浏览器请求JSP对应的Servlet阶段。
如果前面的环节中不会带来编码问题,也就是说在Java虚拟机中运行的时候,
能正常的获取到“中国”,那么在执行servlet的环节中不会“中国”始终是以Unicode存储的中国,那么在第三个环节中需要关注的是
JspWriter如何将数据返回给客户端浏览器。大家可以试验一下,在java中如果用new
String(str.getBytes("encoding"),"encoding")执行的时候,始终不会出现乱码问题,也就是说,一个字符串可以
用不同的代码来getBytes()生成字节数组(底层I18N.jar所作的工作,提供Byte2Char和Char2Byte的转换)。
如果大家可以理解这一点,那么下面大家就需要了解JspWriter输出字符串时采用的编码方式是什么?通过浏览Response.java类可以了解到
Tomcat应用服务器是根据contentType来获取的writer的编码方式,也就是说,最后返回客户端的字节流是contentType对应的
charset中获取出来的字节数组。
4. IE根据返回的数据处理显示阶段
通过前面的分析可以了解到,应用服务器返回的“中国”是根据
ContentType中的charset来显示的,只要IE知道该用这个编码来接收字节流并转成字符串,并将用户的浏览器推荐合适的编码来查看结果,用
户就可以浏览到正确的“中国”两个字。可以高兴得是,目前的IE等浏览器正式这样处理的。
Conclusion
通过上面的分析,我们可以看到,在整个JSP页面的编码过程中,我们真正要解
决的是JSP文件到Java文件这个过程中的编码问题,也就是PageEncoding参数的设置问题。由于pageEncoding参数是
servlet2.3规范中规定的参数,所以下面的方法在很多应用服务器下面都通用,这方面的设置本人在工作中基本上得到了下面的一些方法:
1。在JSP页面的中加上pageEncoding参数,比如:<%@
page contentType="text/html; charset=UTF-8"
pageEncoding="GBK"%>,这样就可以将页面可以用ANSI来存储。也就是说当页面存储的编码方式和chtentType中的
charset不一样的时候,可以考虑加上pageEncoding参数。
2。有些应用服务器(如weblogic),在没有获取到pageEncoding参数的时候,不是先从charset中获取编码类型,而是从另外的一些配置文件,如weblogic.xml文件中加上下面的代码:
<jsp-descriptor>
<jsp-param>
<param-name>compilerSupports</param-name>
<param-value>true</param-value>
</jsp-param>
<jsp-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</jsp-param>
</jsp-descriptor>
(在Tomcat5X种也有类似的处理,在应用的web.xml文件中加上类似下面的配置项)
</jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
常用编码方式分解
GB2312
中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,由国家标准总局发布,1981年5月1日实施,通行于大
陆。新加坡等地也使用此编码。,GB2312-80 是在国内计算机汉字信息技术发展初始阶段制定的,其中包含了大部分常用的一、二级汉字,和 9
区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,这也是最基本的中文字符集。其编码范围是高位0xa1-0xfe,低位也是
0xa1-0xfe;汉字从 0xb0a1 开始,结束于 0xf7fe;大约包含6000多汉字(不包括特殊字符).
GBK
是 GB2312-80 的扩展,是向上兼容的。其编码范围是 0x8140-0xfefe,剔除高位 0x80
的字位。其所有字符都可以一对一映射到 Unicode 2.0,也就是说 JAVA 实际上提供了 GBK 字符集的支持。这是现阶段
Windows 和其它一些中文操作系统的缺省字符集,但并不是所有的国际化软件都支持该字符集,感觉是他们并不完全知道 GBK
是怎么回事。值得注意的是它不是国家标准,而只是规范。随着 GB18030-2000国标的发布,它将在不久的将来完成它的历史使命。
GBK编码是中国大陆制订的、等同于UCS的新的中文编码扩展国家标准。容纳(包含特殊字符)共22014个字符编码
GBK编码是中国大陆制订的、等同于UCS的新的中文编码扩展国家标准。容纳(包含特殊字符)共22014个字符编码
Unicode
采用16位编码体系,其字符集内容与ISO10646的BMP(Basic Multilingual
Plane)相同。Unicode于1992年6月通过DIS(Draf International
Standard),目前版本V2.0于1996公布,内容包含符号6811个,汉字20902个,韩文拼音11172个,造字区6400个,保留
20249个,共计65534个。
UTF-8
俗称万国码,致力于使用统一的编码准则表达各国的文字。为表达更多的文字,utf-8采用2/3混编的方式。目前容纳的汉字范围小于gbk编
码。并且以3字节的方式处理中文,带来了兼容性的问题,原有的gbk,gb2312,gb18030编码文件都不能正常的处理。因其变宽编码,使编程变成
变得困难和复杂,因为即使是最基本的字符处理函数也要分别检查每一字节,以分辨字符边界。这就降低了处理速度,并需要复杂、易错的代码。
附:相关编码方式及关系
Unicode:
unicode.org制定的编码机制, 要将全世界常用文字都函括进去.
在1.0中是16位编码, 由U+0000到U+FFFF. 每个2byte码对应一个字符; 在2.0开始抛弃了16位限制, 原来的16位作为基本位平面, 另外增加了16个位平面, 相当于20位编码, 编码范围0到0x10FFFF.
UCS:
ISO制定的ISO10646标准所定义的 Universal Character Set, 采用4byte编码.
Unicode与UCS的关系:
ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始, unicode采用了与ISO
10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值, 使得两者保持一致.
UCS的编码方式:
UTF: Unicode/UCS Transformation Format
* 与CPU字节顺序无关, 可以在不同平台之间交流
* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码)
UTF-16是unicode的preferred encoding.
UTF与unicode的关系:
Unicode是一个字符集, 可以看作为内码.
而UTF是一种编码方式, 它的出现是因为unicode不适宜在某些场合直接传输和处理. UTF-16直接就是unicode编码, 没有变换, 但它包含了0x00在编码内, 头256字节码的第一个byte都是0x00, 在操作系统(C语言)中有特殊意义, 会引起问题. 采用UTF-8编码对unicode的直接编码作些变换可以避免这问题, 并带来一些优点.