经常在讨论区看到有人问我的JSP显示中文有乱码怎么办,我用request得到的用户输入的中文怎么是乱码,我把汉字写到数据库怎么是乱码,等等一些关于汉字乱码的问题。
其实这个问题很简单,管它汉字不汉字,还是日文,还是其他的什么双字节的语言,我们一律把它当作UTF-8看待。
(一)request中的双字节文字
好下面我们就来实现在整个应用程序中使用UTF-8编码工作,之所以选择UTF-8不仅仅之于上述原因,我们知道java的就是基于在UTF-8之上的,所以我们选择UTF-8应该没错^_^
我们首先把我们的.java, .jsp文件都用UTF-8编码来保存,如果以前的没有用UTF-8保存也无所谓,但是建议以后写的都用UTF-8来保存。
并在.jsp里面写:<%@page contentType="text/html; charset=UTF-8"%>而不是<%@page contentType="text/html; charset=UTF-8"%>
然后在web.xml添加下面一段:
<web-app>
...
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>com.redv.projects.eduadmin.util.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
其中com.redv.projects.eduadmin.util.filters.SetCharacterEncodingFilter的代码如下:
package com.redv.projects.eduadmin.util.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SetCharacterEncodingFilter
implements Filter {
protected String encoding = null;
protected FilterConfig filterConfig = null;
protected boolean ignore = true;
public void destroy() {
this.encoding = null;
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Conditionally select and set the character encoding to be used
if (ignore || (request.getCharacterEncoding() == null)) {
String encoding = selectEncoding(request);
if (encoding != null) {
request.setCharacterEncoding(encoding); //就是这句话在工作的啦,哈哈,它:Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader().
}
}
// Pass control on to the next filter
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null) {
this.ignore = true;
}
else if (value.equalsIgnoreCase("true")) {
this.ignore = true;
}
else if (value.equalsIgnoreCase("yes")) {
this.ignore = true;
}
else {
this.ignore = false;
}
}
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
}
这样,我们的request请求就是以UTT-8编码的,在JSP程序中就可以使用:request.getParameter("myKey")来直接得到UTF-8编码的字符串了,而不需要像这样:new String(request.getParameter("myKey").getBytes("ISO-8859-1"), "GBK")来解决那些乱码了。
(二)数据库处理的双字节文字
另外一个,就是写入数据库的问题,我们知道我们在使用mysql的时候可以改用这样的url来处理汉字编码问题:jdbc:mysql://localhost:3306/upas?useUnicode=true&characterEncoding=gb2312,
那么对于那些我们无法像mysql这样解决的怎么办呢?难道我们每次都这样写吗:
import java.sql.*;
Class.forName("org.gjt.mm.mysql.Driver");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "");
pstmt = con.prepareStatement("SELECT f3, f4 FROM tbl1 WHERE f1 = ? AND f2 = ?");
pstmt.setString(1, new String(f1.getBytes("GBK"), "ISO-8859-1");
pstmt.setString(2, new String(f2.getBytes("GBK"), "ISO-8859-1");
rs = pstmt.executeQuery();
String f3, f4;
while(rs.next()) {
f3 = new String(rs.getString(1).getBytes("ISO-8859-1"), "GBK");
f4 = new String(rs.getString(2).getBytes("ISO-8859-1"), "GBK");
}
}
finally {
//close resouces
...
}
其实我们完全可以这样写:
import java.sql.*;
import com.redv.sql.encoding.*;
Class.forName("org.gjt.mm.mysql.Driver");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "");
//接管数据库连接实例
boolean coding = true;
EncodingConnection codingConnection = new EncodingConnection(con, coding, "ISO-8859-1", "GBK");
//获得接管后的数据库连接实例,以后直接使用con已经是经过EncodingConnection重新包装过的实例
con = codingConnection.getConnection();
pstmt = con.prepareStatement("SELECT f3, f4 FROM tbl1 WHERE f1 = ? AND f2 = ?");
pstmt.setString(1, f1);
pstmt.setString(2, f2);
rs = pstmt.executeQuery();
String f3, f4;
while(rs.next()) {
f3 = rs.getString(1);
f4 = rs.getString(2);
}
}
finally {
//close resouces
...
}
看看,怎么样,我们只需要在获取数据库连接的地方稍微修改一下,甚至我们可以把它当作参数保存在 properties里面,改变coding的布尔值来设定是否使用自动编码转换。常常我们可以使用一个Database类来封装获取数据库连接的那段getConnection,以便于我们可以从 javax.sql.DataSource中获取到数据库连接。这个时候我们仅仅需要修改我们的Database类即可,而不用去搜索所有使用了rs.setString(), rs.getString()的地方去加入我们的编码转换代码了。甚至我们在使用con.createStatment()语句时,即使我们sql语句含有汉字或者其它的双字节字符时一样没有问题:
SELECT 姓名, 性别 FROM 学生表 WHERE 班级 LIKE '%计算机%'
在介绍方法之前我们首先应该清楚具体的问题有哪些,笔者在本博客当中论述的JSP中文乱码问题有如下几个方面:页面乱码、参数乱码、表单乱码、源文件乱码。下面来逐一解决其中的乱码问题。
一、JSP页面中文乱码
在JSP页面中,中文显示乱码有两种情况:一种是HTML中的中文乱码,另一种是在JSP中动态输出的中文乱码。
先看一个JSP程序:
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" %>
- <html>
- <head>
- <title>中文显示示例</title>
-
- </head>
- <body>
- 这是一个中文显示示例:
- <%
- String str = "中文";
- out.print(str);
- %>
- </body>
- </html>
上面这个JSP程序看起来好像是在页面显示几句中文而且标题也是中文。运行后在浏览器中显示如图所示
原因在于没有在JSP中指定页面显示的编码,消除乱码的解决方案很简单上面代码中page命令修改成如下所示即可
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" contentType="text/html; charset=GB2312" %>
- <html>
- <head>
- <title>中文显示示例</title>
-
- </head>
- <body>
- 这是一个中文显示示例:
- <%
- String str = "中文";
- out.print(str);
- %>
- </body>
- </html>
再次运行乱码消失,原理就是向页面指定编码为GB2312,那么页面就会按照此编码来显示,于是乱码消失。
二、URL传递参数中文乱码
一般情况下在使用get方法提交表单的时候传递的参数如果是中文的话很可能会出现乱码。
下面是一个示例程序
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" contentType="text/html;charset=gb2312"%>
- <html>
- <head>
- <title>URL传递参数中文处理示例</title>
- </head>
- <%
- String param = request.getParameter("param");
- %>
- <body>
- <a href="URLCharset.jsp?param='中文'">请点击这个链接</a><br>
- 你提交的参数为:<%=param%>
- </body>
- </html>
上面这个JSP程序的功能就是通过一个URL链接向自身传递一个参数,这个参数是中文字符串,这个程序的运行效果如下图
对于URL传递中文参数乱码这个问题,其处理方法比较特殊,仅仅转换这个中文字符串或者设置JSP页面显示编码都是不能解决问题的,需要修改Tomcat服务器的配置文件才能解决问题。在这里修改Tomcat的conf目录下的server.xml配置文件,具体改后的代码如下
[html] view plain copy
print?
- <Connector port="8080" protocol="HTTP/1.1" URIEncoding="gb2312"
- connectionTimeout="20000"
- redirectPort="8443" />
在原来代码中添加URI编码设置URIEncoding=“gb2312”即可,重启Tomcat服务器可以得到正确的页面。其原理也和上面的情况类似,就是向程序指明编码类型,然后显示就正常了。
三、表单提交中文乱码
对于表单的数据可以使用request.getParameter(“”)的方法获取,但是当表单中出现中文数据的时候就会出现乱码。
示例代码如下
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" contentType="text/html;charset=gb2312"%>
- <html>
- <head>
- <title>Form中文处理示例</title>
- </head>
- <body>
- <font size="2">
- 下面是表单内容:
- <form action="AcceptFormCharset.jsp" method="post">
- 用户名:<input type="text" name="userName" size="10"/>
- 密 码:<input type="password" name="password" size="10"/>
- <input type="submit" value="提交">
- </form>
- </font>
- </body>
- </html>
在上面的表单当中想AcceptFormCharset这个页面提价两项数据,下面是AcceptFormCharset.jsp的内容:
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*"
- contentType="text/html;charset=gb2312"%>
- <html>
- <head>
- <title>Form中文乱码</title>
- </head>
- <body>
- <font size="2"> 下面是表单提交以后用request取到的表单数据:<br>
- <%
- String userName = request.getParameter("userName");
- String password = request.getParameter("password");
- out.println("表单输入userName的值:" + userName + "<br>");
- out.println("表单输入password的值:" + password + "<br>");
- %>
- </font>
- </body>
- </html>
在上面的程序中,如果表单输入没有中文,则可以正常的显示当输入的数据中有中文的时候,得到的结果如图所示。
产生种结果的原因是Tomcat中对于post方法提交的表单采用的默认编码为ISO-8859-1,而这种编码格式不支持中文字符。对于这个问题可以采用转换编码格式的方法来解决,现在对AcceptFromCharset这个页面改动如下:
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*"
- contentType="text/html;charset=gb2312"%>
- <html>
- <head>
- <title>Form中文乱码</title>
- </head>
- <body>
- <font size="2"> 下面是表单提交以后用request取到的表单数据:<br>
- <%
- String userName = request.getParameter("userName");
- String password = request.getParameter("password");
- out.println("表单输入userName的值:" + new String(userName.getBytes("ISO-8859-1"), "gb2312")+ "<br>");
- out.println("表单输入password的值:" + new String(password.getBytes("ISO-8859-1"), "gb2312")+ "<br>");
- %>
- </font>
- </body>
- </html>
经过这样的转换编码以后,所有的中文输入都可以用request对象正常取出。在上面这个程序中,第四行和第五行是转换编码格式的关键,先从ISO-8859-1格式的字符串中取出字节内容,然后在用GB2312的编码格式重新构造一个新的字符串。这样就可以支持中文变淡输入的正常取值和显示。改进以后程序运行结果如下
经过上面的更改编码格式的处理,表单的中文输入乱码问题已经得到解决。但是如果上面的表单中的输入项不止是两个,那么每个输入项都需要进行编码转换,那样就很麻烦了。这是我们就用到了大名鼎鼎的过滤器filter了。关于这里的内容大致的思虑和上面的一样具体做法请参照笔者的另一篇文章。
四、Eclipse中JSP文件中文乱码
在Eclipse或者MyEclipse中由于默认的JSP编码格式为ISO-8859-1,所以当打开由其他编辑器编辑的JSP文件时会出现乱码,如图所示
对于这个问题我们只需要更改一下Eclipse或者是MyEclipse中对JSP的默认编码就可以了,修改的地方(我的MyEclipse版本为11)如图所示
PS
在Eclipse或者MyEclipse当中JSP文件默认的编码为ISO-8859-1,所以在JSP代码中间如果出现中文就不能保存,例如如下代码
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" %>
- <html>
- <head>
- <title>中文显示示例</title>
-
- </head>
- <body>
- 这是一个中文显示示例:
- <%
- String str = "中文";
- out.print(str);
- %>
- </body>
- </html>
修改后在保存的时候会提示如下:
现这个提示的原因在于JSP源文件中有ISO=8859-1编码无法识别的中文字符,对于这个问题,解决办法就是在JSP页面中声明页面编码格式即可。声明后代码如下:
[java] view plain copy
print?
- <%@ page language="java" import="java.util.*" pageEncoding="GB2312" %>
- <html>
- <head>
- <title>中文显示示例</title>
-
- </head>
- <body>
- 这是一个中文显示示例:
- <%
- String str = "中文";
- out.print(str);
- %>
- </body>
- </html>
其中第一行中pageEncoding=“gb2312”指明了JSP页面编码采用GB2312,这样就可以正常保存JSP的源文件了。
遇到问题首先分析问题出现的原因,只有知道了原因才能去解决,学习分析问题的来源远比解决这个问题重要的多。
乱码问题的原因就是程序(Eclipse也好,浏览器也罢)的编码没有和编程人员的编码进行统一,(就像你和一个不懂中文的人用中文交流他当然不懂了)那么解决这个问题只需要将编程人员想要的编码告诉程序就可以了,以上解决乱码问题的种种方法都可以说是一种声明编码的过程,也就是说乱码问题终极解决方案就是:转码。这里的转码要么是编程人员手动转,要么就是声明一下让程序去转,换句话说就是:和不懂中文的交流,要么让他学中文,要么你就去学习他的语言。