深入JAVA WEB 内幕学习系列 三、 深入分析Java Web中的中文编码问题
一、为什么要编码
- 在计算机中存储信息的最小单元是一个字节,即8bit,所以最多能表达的字符范围是0-255个
- 人类使用的符号太多,无法用一个字节完全表示
- 因此需要一个新的数据结构char ,从char到byte必须编码
二、 常见编码
- ASCII 码
总共128个,用1个字节的低7位表示,0-31是控制字符,如回车、换行,32-126是打印字符,可以通过键盘输入并且能够表示出来。 - ISO-8859-1
单字节8位编码,可以表示256个字符。 - GB2313
双字节编码,包含6763个字符。 - GBK
为了扩展GB2312,同时兼容GB2312。用GB2312编码的汉字可以用GBK解码。 - GB18032
国标,与GB2312兼容,但实际应用并不广泛。 - UTF-16
统一码,使用两个定长字节表示。 - UTF-8
采用变长两个字节表示。
编码之间的比较:
GBK比BG2312能够处理更多的汉字,两者之间选择GBK.UTF-8与UTF-16相比,UTF-16编码效率更高,从字符到字节的转换更简单,进行字符操作也更好,适合本地磁盘合内存之间的编码,但不适合在网络间传输,因为网络传输中容易损坏字节流,一旦字节流损坏将很难回复,所以相比较UTF-8更适合网络传输,因为UTF-8对ASCII字符采用单字节存储,单个字符损坏不会影响后面的,编码效率介于GBK与UTF-16,因此UTF-8是比较理想的中文编码。
三、 Java Web中涉及的编码
用户从浏览器端发起一个HTTP请求,需要存在编码的地方是URL、Cookie、Paramiter。服务器端可能还需要读取数据中的数据,本地或网络中其它地方的文本文件,都有可能存在编码问题。
3.1 URL的编解码
URL和URI 分别对应的是Servlet中的request.getRequestURL()和request.getRequestURI(),Port对应在Tomcat的中配置,而ContextPath 在
3.2 POST表单的编解码
提交表单时,浏览器首先会根据ContentType的编码格式进行编码,然后提交到服务端,服务端根据ContentType的编码字符集进行解码,所以一把不会出现乱码,而且我们自己可以设置整个字符集request.setCharacterEncoding(charset)来设置;
注意该方法必须在第一次调用request.getParameter()之前,调用,因为request.getParameter()会进行解码,这时我们设置的字符集还没生效,默认使用ISO8859-1解码,导致出现乱码。
为什么request.setCharacterEncoding(charset)对GET请求的参数不生效,因为get请求的编码是根据浏览器的设置进行编码,一般为UTF-8.而解码默认不使用ContentType的字符集,而是使用ISO8859-1,所以即便通过request.setCharacterEncoding(charset)设置了字符集,对GET请求的参数一样不生效,我们可以通过将connector中的中的useBodyEncodingForURI设置为true,来使用ContentType的字符集进行解码。对get请求的参数,我们需要对其使用ISO8859-1重新编码,然后使用UTF-8进行解码,即可解决乱码问题。
3.3响应乱码问题
我们可以使用response.setCharacterEncoding(charset)来设置,如果没有进行设置ContentType的字符集,默认使用HTML页面的字符集。
如果是数据库,我们可以通过设施JDBC URL来指定例如在后面加上characteEncoding=utf-8。
3.4 JS的URL编码
JS中处理URL编码的函数有三个,掌握了这三个函数就能基本处理JS中的URL乱码问题。
1、escape();
这个函数是将ASCII字母、数字、标点符号之外的其它字符进行Unicode编码并在编码值前加%u。
在最新的ECMAScript v3标准中已经将该方法删除,使用下面的两个方法进行替代。
2、encodeURI()
编码字符范围更多,使用UTF-8编码。
3、encodeURIComponent()和decodeURIComponent()
这两个函数对应的分辨是编码和解码,比encodeURI()编码更加彻底,这个函数通常是为了将一个URL当作参数放在另一个URL中,如果不进行编码,后面URL中的&附后会影响前面URL的完整性,而encodeURI()方法做不到。
3.5 JAVA与JS的编解码问题
JAVA中对应的URL编解码有两个类URLEncoder和URLDecoder ,这两个类使用的是服务器的字符集进行编解码。如果使用encodeURIComponet()进行编码,到服务端使用URLDecoder解码有可能出现乱码,因为前端使用的是UTF-8编码,而服务端有可能是GBK或者GB2313。解决办法是使用encodeURIComponet()两次编码即可。