Servlet&JSP的那些事儿(二十二)

时间:2021-07-24 17:06:43

本篇的主题是中文乱码问题.我们将分析中文乱码产生的原因,并通过多种方式解决中文乱码问题。

在计算机中,只有二进制数据。不管数据保存在内存或者外存,对于我们看到的字符,也是以二进制形式保存的。不同字符对应二进制数的规则,就是字符的编码。字符编码的集合就称之为字符集。

常用字符集

常用的字符集有ASCII码,ISO8859-1,GB2312,GBK,Unicode,UTF-8。每一个ASCII码用8位二进制数表示,比如字符“0”表示为48。ASCII最多能表示256种字符,最初只用于表示英文字母和其他字符,但是随着计算机发展与普及,其他国家都需要引入本地语言,所以明显不够用。ISO8859-1则扩充了ASCII码,增加了一些其他的西文字符。GB2312是中国国家标准汉字信息交换用编码,GBK对GB2312提供扩展支持,除了完全兼容GB2312外,还支持繁体字,不常用汉字和许多符号。不过GBK并不是国家标准,它只是规范。

每个国家都定义了自己的字符集。如果你发送一封邮件给一个德国朋友,邮件发送时按照汉字编码转换为二进制数据,等到他接收到以后再将二进制数据转换为德文,那就会出现乱码。因为,在不同的字符集之间,同一个数字可能对应了不同的符号,也可能在另一种字符集中,没有对应的符号。为了解决这些问题,Unicode协会制定了Unicode编码。unicode使用双字节无符号数对每一个字符进行编码。范围为0-65535,目前已定义了40000多个不同的unicode字符。UTF-8是为了减少存储和传输英文字符的数据量。因为unicode编码一个字符要占两个字节,而在网络上,大多数信息是用英文表示的。

对乱码产生原因的分析

为了让使用java编写的程序能在各种语言平台下运行,java在内部使用unicode字符集来表示字符,这样就存在unicode字符集和本地字符集进行转换的过程。在java中读取本地字符数据时,需要将其转换为unicode编码,而在输出时,需要将unicode编码转换为本地字符编码。例如在中文系统中从控制台读取一个字符“中”,实际上读取的是它的GBK编码0xD6D0,在java语言中药将GBK转换为unicode编码0x4E2D,此时,内存中存储的字符“中”对应的数值为0x4E2D,在向控制台输出时,则将其转换为GBK。

根据上述过程,转换过程是可逆的,不应该存在乱码问题才对。但实际上,web应用中,浏览器,web服务器,web应用程序,数据库中都可能使用不同的字符集,从而导致字符在各种不同字符之间转换时,出现乱码问题。

当从unicode字符集向某个字符集转换时,如果该字符集没有对应的编码,则得到0x3f(即问号?)。从其他字符集向unicode编码转换时,如果这个二进制数在该字符集中没有标识任何字符,则得到的结果是0xfffd。

中文乱码问题解决方案

以post方法提交的表单数据中有中文字符

由于web容器默认采用ISO-8859-1编码,在servlet/jsp中,通过请求对象的getParameter()方法得到的字符串是以ISO-8859-1转换而来,这是导致乱码的原因之一,为了避免容器以ISO-8859-1的编码格式返回字符串,对于以POST方法提交的表单数据,可以在获取请求参数前,调用request.setCharacterEncoding("GBK") ,明确指出请求正文使用的编码格式是GBK,在向浏览器发送数据前,调用指定输出内容的编码方式是GBK。

对于JSP页面,在获取请求参数值前,写上下面的代码:

<%request.setCharacterEncoding("gb2312");%>

为了指定输出内容的编码格式,设置page指令的contentType属性。

<%reponse.setContentType("text/html;charset=GBK");%>

在web容器转换jsp页面后的servlet类中,会自动添加下面的代码:

reponse.setContentType("text/html;charset=GBK");

以get方式提交的表单数据有中文字符

当提交表单采用get方式时,提交的数据作为查询字符串被附加到URL的末端,发送到服务器,此时在服务器端调用setCharacterEncoding就没用了,我们需要在得到请求参数的值后,自己做正确的编码转换。

String name = request.getParameter("name");
name = new String(name.getBytes("ISO-8859-1","GBK"));

在数据库中存储的读取中文数据

要解决读取数据库中中文数据出现乱码的问题,只需要将数据库默认的编码格式改为GBK或GB2312即可。

使用过滤器解决中文乱码问题

过滤器代码如下:

package com.shan.filter;

import java.io.IOException;
import javax.servlet.*;

public class EncodingFilter implements Filter {
	private String encoding = null;
	private FilterConfig filterConfig = null;
	
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		this.encoding = filterConfig.getInitParameter("encoding");
	}
	
	public void destroy() {
		this.encoding = null;
		this.filterConfig = null;
	}
	
	public void doFilter(ServletRequest request, ServletResponse response, 
	FilterChain chain) throws IOException, ServletException {
		if(null != encoding){
			request.setCharacterEncoding(encoding);
		}
		chain.doFilter(request, response);
	}
}

web.xml配置文件内容如下:

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0"
  metadata-complete="true">

  <filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.shan.filter.EncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>gb2312</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

让Tomcat支持中文文件名

在web应用程序中我们可能会使用具有中文文件名的文件,然而Tomcat在默认情况下不能访问中文文件名的文件。为了让Tomcat可以正常访问,需要配置在Tomcat安装目录下的conf文件夹下的server.xml文件。找到<Connector>元素,在该元素中添加URIEncoding属性,将其值设置为UTF-8,如下所示:

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8"/>

绝大多数浏览器在传输URI时都以UTF-8编码。在web应用程序中,如果调用response.sendRedirect()方法重定向到中文文件名的页面,需要用如下方式调用:

response.sendRedirect(java.net.URLEncoder.encode("中文文件.html","UTF-8"));

除此之外,对于其他访问方式,可直接写中文文件名,不需要进行编码。

转载请注明出处:http://blog.csdn.net/iAm333