程序中中文乱码问题的总结

来源:互联网 发布:矩阵分析与计算 pdf 编辑:程序博客网 时间:2024/05/22 20:28

近日在项目中遇到了中文乱码问题,前前后后花了两三天时间才得以解决。现对程序中中文出现乱码的可能原因及解决方案做个简单总结。


1、开发环境:Win7  Eclipse  Spring+Struts2+Hibernate  Tomcat MySQL


2、文件编码:Text file encoding

项目中的java、jsp等文件均采用UTF-8编码,设置方式:

Project --> Properties --> Resources --> Text file encoding 或者

Window --> Preferences --> General --> Workspace --> Text fileencoding

 

3、jsp文件中pageEncoding与contentType属性

一般地,我们都会在jsp文件的开头加上这样的声明:

<%@ page language="java"pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>

那pageEncoding属性和contentType的charset属性之间到底有什么区别呢?简单地讲,pageEncoding指的是jsp文件本身的编码,而contentType的charset的作用是告诉客户端(浏览器)我的页面是采用什么样的方式进行编码,相应的客户端(浏览器)就应该采用什么样的方式进行解码。

下面我们再简单分析下jsp的执行过程,这样就能更好地理解pageEncoding和contentType的charset之区别。jsp文件的执行过程如下:

(1)jsp编译成.java。它会根据pageEncoding的设定编码格式读取jsp,结果是由指定的编码方案翻译成统一的UTF-8编码的java源码(即.java)。

(2).java编译成.class。将第一步编译的UTF-8编码的java源码编译成UTF-8编码的二进制码(即.class)。

(3)Tomcat执行第二步编译好的二进制码,输出结果,并告诉浏览器我是以何种方式编码的(contentType的charset属性)。

应该注意的是,contentType的默认设定为"text/html;charset=ISO-8859-1",也就是说默认是ISO-8859-1编码格式。


4、Tomcat配置文件中的URIEncoding和useBodyEncodingForURI属性

对于以GET方式发送的请求中包含中文参数的情形,可能经常会出现乱码现象。这是因为Tomcat默认是按ISO-8859-1的方式对URL进行解码,而ISO-8859-1并未包括中文字符,这样的话中文字符肯定就不能被正确解析了。

解决方法:tomcat安装目录 --> conf --> server.xml,设置useBodyEncodingForURI或者URIEncoding属性:

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

URIEncoding与useBodyEncodingForURI的区别:

(1)URIEncoding的作用是对GET请求中的参数按照设定的方式进行编码,如UTF-8;而useBodyEncodingForURI="true"的作用是指定请求参数的编码采用请求体的编码方式。

(2)两个属性选其一进行配置即可。URIEncoding参数的配置具有全局性,它指定对所有GET方式请求进行统一的编解码;而useBodyEncodingForURI具有更大的灵活性,由于不用的页面可以采取不同的编码方式,因而请求参数也就可以随着页面编码方式的变化而变化。


5、web.xml中相关的配置

对于以POST方式发送的请求中包含中文参数的情形,我们应该怎么办呢?

(1)最直接的办法就是在action中指定请求参数的编码方式,例如:

request.setCharacterEncoding("UTF-8")

(2)在web.xml文件中配置过滤器CharacterEncodingFilter,例如:

<filter>    <filter-name>encodingFilter</filter-name>    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>    <init-param>        <param-name>encoding</param-name>        <param-value>utf-8</param-value>    </init-param>    <init-param>        <param-name>forceEncoding</param-name>        <param-value>true</param-value>    </init-param></filter>

 

以上是最常遇到的项目中关于中文乱码的情形。下面讲讲本人在开发过程中遇到的怪异之事。本地项目是在eclipse中通过tomcat进行发布,运行时一切顺利。可是把项目打成war包发布在服务器上时,就是莫名其妙地出现乱码,期间尝试过上述所有办法,无效!

由于是一个maven项目,考虑是不是在打包的时候出现的编码问题,于是检查pom.xml的配置,发现以下配置没有问题。

<properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>    <maven.compiler.encoding>UTF-8</maven.compiler.encoding></properties>

接着尝试在代码中加入以下语句:

log.info(System.getProperty("file.encoding"));

结果在日志中惊人地发现输出竟然是GBK,也就是说当前文件的编码格式是GBK!!怎么可能呢?再次检查文件的属性,file encoding确实是UTF-8,maven打包的时候也指定了UTF-8的编码格式,其他环节也都没问题,也就是说最关键的问题就是不知道什么原因、在哪个地方把源文件的编码变成了GBK,所以就出现了乱码。

通过反复百度,最终找到了问题的解决办法:

添加环境变量,变量名:JAVA_TOOL_OPTIONS,值:-Dfile.encoding=UTF-8

 

反思:问题虽然解决了,但哪还是不清楚产生问题的原因,为什么本地在中eclipse通过tomcat进行发布能够正常运行,而服务器上通过tomcat启动就会出现乱码?总之,问题的产生肯定和file.encoding参数有关。以下是关于file.encoding的相关解释:

Java's file.encoding property on Windows platfor

This property is used for the default encoding inJava, all readers and writers would default to using this property.file.encoding is set to the default locale of Windows operationg system sinceJava 1.4.2. System.getProperty("file.encoding") can be used to accessthis property. Code such as System.setProperty("file.encoding","UTF-8") can be used to change this property. However, the defaultencoding can be not changed dynamically even this property can be changed. Sothe conclusion is that the default encoding can't change after JVM starts. java-Dfile.encoding=UTF-8 can be used to set the default encoding when starting aJVM.

大意就是,默认字符集是在java虚拟机启动时决定的,依赖于JVM所在的操作系统的区域以及字符集,这就决定了JVM会采用何种编码方式对文件进行编解码,当然我们可以通过配置环境变量的方式来修改这个默认值。

那么是不是可以这样理解,直接通过tomcat启动的项目,文件本身设置的Text fileencoding对虚拟机来说并没有多大用处,如果我们没有配置file.encoding参数,虚拟机就会采用默认的编码方式和字符集对java源文件进行编译运行?至于在eclipse中运行正常,是不是eclipse在什么地方读取了文件的Text file encoding属性,然后告诉虚拟机采用指定的方式进行编解码呢?

 

附:jdk中出现的与file.encoding有关的类

com.sun.org.apache.xml.internal.serializer.Encodings.getMimeEncoding(String)

java.net.URLEncoder的static块中

java.nio.charset.Charset.defaultCharset()

javax.print.DocFlavor的static块中

所有这些地方最终都涉及到java.security.AccessController.doPrivileged(),很可惜,它是一个native方法,要想研究,只能深入底层、深入到虚拟机了!

public static native <T> T doPrivileged(PrivilegedAction<T> action);

1 0
原创粉丝点击