本篇的主题是中文乱码问题.我们将分析中文乱码产生的原因,并通过多种方式解决中文乱码问题。
在计算机中,只有二进制数据。不管数据保存在内存或者外存,对于我们看到的字符,也是以二进制形式保存的。不同字符对应二进制数的规则,就是字符的编码。字符编码的集合就称之为字符集。
常用字符集
常用的字符集有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