java/jsp的字符集是老问题了,这里谈一下我的理解。
我们简单点拿一个"中"字来说,它的gb2312编码十六进制字符串是d6 d0(下面均以十六进制表示),ASCII码对应gb2312不变,所以中文在gb2312中是双字节,ASCII是单字节。
那么当java把d6 d0理解为gb2312编码时它对应的字符集编码是
unicode: 4e 2d
utf-8: e4 b8 ad
iso: 3f
因为单字节iso对应unicode双字节,而4e 2d在iso-map-unicode表中找不到,故对应单字节3f,打印出来即“?”
当java把d6 d0理解为iso8859-1编码时它对应的字符集编码是
unicode: 00 d6 00 d0
utf-8: c3 96 c3 90
gb2312: 3f 3f
因为00 d6和00 d0在gb2312-map-unicode表中找不到,故对应两个3f,打印出来即“??”
下面是一段名词介绍。
jsp-pageEncoding:指定jspc来解释jsp成servlet时使用的字符集,这个属性会影响到页面中写定的中文字符的输出结果。
jsp-charset:指定jsp的out输出时的字符集,out的输出函数将会使用该字符集转换内存中数据。
jspc-memory(unicode):内存中都是unicode。
servlet/class(utf-8):硬盘上的servlet源文件和编译后的class文件,都是utf-8。
out:会根据jcs来转换内存中数据。
brower(iso8859-1):客户端浏览器使用iso8859-1字符集来解析服务器返回的数据,默认是jsp-charset。
brower(gb2312):客户端浏览器使用gb2312字符集来解析服务器返回的数据,默认是jsp-charset。
来看看服务器端页面中直接输出"中"字的情形,如
在中文windowns xp(默认gb2312编码)下用ultraEdit打开一个jsp敲入out.println("中");并不做编码转换,则用十六进制显示可以看到"中"对应d6 d0。
第一种情况:
<%@ page pageEncoding="iso8859-1" %>
<%@ page contentType="text/html;charset=iso8859-1" %>
读jsp到内存中(unicode):00 d6 00 d0
servlet/class(utf-8):c3 96 c3 90
读class到内存中(unicode):00 d6 00 d0
out(iso8859-1):d6 d0
浏览器(iso8859-1):⊙ ⊙
浏览器(gb2312):中
上面恰巧的是d6 d0如果当作iso8859-1输入又再当作iso8859-1输出则正好又变回d6 d0,客户端用gb2312显示正好是"中"。
第二种情况:
<%@ page pageEncoding="iso8859-1" %>
<%@ page contentType="text/html;charset=gb2312" %>
读jsp到内存中(unicode):00 d6 00 d0
servlet/class(utf-8):c3 96 c3 90
读class到内存中(unicode):00 d6 00 d0
out(gb2312):3f 3f
浏览器(iso8859-1):? ?
浏览器(gb2312):? ?
第三种情况:
<%@ page pageEncoding="gb2312" %>
<%@ page contentType="text/html;charset=iso8859-1" %>
内存中(unicode):4e 2d
servlet/class(utf-8):e4 b8 ad
内存中(unicode):4e 2d
out(iso8859-1):3f
浏览器(iso8859-1):?
浏览器(gb2312):?
第四种情况:
<%@ page pageEncoding="gb2312" %>
<%@ page contentType="text/html;charset=gb2312" %>
内存中(unicode):4e 2d
servlet/class(utf-8):e4 b8 ad
内存中(unicode):4e 2d
out(gb2312):d6 d0
浏览器(iso8859-1): ⊙ ⊙
浏览器(gb2312):中
服务器端接受get请求带参数输出"中"字的情形,如
在浏览器地址栏敲入http://localhost/?name=中然后回车
tomcat默认当作iso8859-1字符集处理。这时候pageEncoding对请求过来的参数没有作用。
第一种情况:
<%@ page contentType="text/html;charset=iso8859-1" %>
内存中(unicode):00 d6 00 d0
out(iso8859-1):d6 d0
浏览器(iso8859-1): ⊙ ⊙
浏览器(gb2312):中
第二种情况:
<%@ page contentType="text/html;charset=gb2312" %>
内存中(unicode):00 d6 00 d0
out(gb2312):3f 3f
浏览器(iso8859-1):? ?
浏览器(gb2312):? ?
但是如果经过(大家经常用到)
name = new String(name.getBytes("iso8859-1", "gb2312");
结果是
第一种情况:
<%@ page contentType="text/html;charset=iso8859-1" %>
内存中(unicode):00 d6 00 d0
new String(unicode):4e 2d
out(iso8859-1):3f
浏览器(iso8859-1):?
浏览器(gb2312):?
第二种情况:
<%@ page contentType="text/html;charset=gb2312" %>
内存中(unicode):00 d6 00 d0
new String(unicode):4e 2d
out(gb2312):d6 d0
浏览器(iso8859-1): ⊙ ⊙
浏览器(gb2312):中
同理可以分析utf8。
像上面那样把每一次数据的编码转换都分析清楚,解决乱码问题就好办了。
上面的分析都基于一个前提:中文windows下提交数据都是以gb2312编码,但IE的”高级“中有一个“总是使用utf8提交数据”的选择,我勾上后get提交,却没有出现理解中的乱码,接着使用Proxy抓取tcp数据包分析依然看到是gb2312的d6 d0,而不是utf8的e4 b8 ad,也许说的是post提交,我再试试,下篇文章讨论这个和数据库字符集问题。