文章目录
-
- 乱码问题
-
- [1. 判断字符串的编码格式](#1. 判断字符串的编码格式)
- [2. 获取 GET 请求中的数据,打印乱码](#2. 获取 GET 请求中的数据,打印乱码)
- [3. 获取 POST 请求中的数据,打印乱码](#3. 获取 POST 请求中的数据,打印乱码)
- [4. 中文字符串,输出到页面显示乱码](#4. 中文字符串,输出到页面显示乱码)
- [5. setContentType 和 setCharacterEncoding](#5. setContentType 和 setCharacterEncoding)
乱码问题
1. 判断字符串的编码格式
注意,由于存在重码现象,以下方案并不严谨 。
java
// 由于字符编码存在重叠区,所以一个字符/字符串有多种编码可能,是完全正常合理的。
public static String getEncoding(String str) {
String encode[] = new String[]{
"ASCII",
"ISO-8859-1",
"GB2312", // GB2312 是 GBK 的一种「具体情况」。
"GBK",
"UTF-8", // UTF-8 是 Unicode 的一种「具体情况」。
"UTF-16", // UTF-16 是 Unicode 的一种「具体情况」。
"Unicode",
};
String ret = "";
for (int i = 0; i < encode.length; i++) {
try {
if (str.equals(new String(str.getBytes(encode[i]), encode[i]))) {
ret = ret + encode[i] + " ";
}
} catch (Exception ex) {
}
}
return ret.equals("") ? "Other" : ret;
}
乱码出现在三种场景:
-
GET 请求提交的中文数据,在 Servlet 中输出乱码。
-
POST 请求提交的中文数据,在 Servlet 中输出乱码。
-
返回给浏览器的中文数据,在页面显示乱码。
提前声明,虽然同样是提交的数据在 Servlet中 显示为乱码,但是 GET / POST 发生乱码的原因和解决乱码的方案并不相同,不能混用 。
2. 获取 GET 请求中的数据,打印乱码
默认情况下,浏览器发送给 Tomcat 的数据都是以 ISO-8859-1
进行编码后的字节流。
在 get 请求中,Tomcat 以何种方式看待、解析接收的这些字节流取决于 server.xml 中的一个配置: <Connector port="8080" ... URIEncoding="xxx"
。
默认情况下 URIEncoding="ISO-8859-1"
,即 Tomcat 默认以 ISO-8859-1 编码解析所收到的 get 请求发送来的字节流,从而转换为字符流,即字符串。
但是这里有一个关键性问题:Java 字符串是 UTF-8 编码。所以,当你通过 req.getParameter("");
获得一个中文字符串时,这个字符串是 ISO-8859-1 编码,但一旦你使用 System.out.println()
打印时,Java 会当它是一个 UTF-8 编码的字符串。这就是出现乱码原因。
解决问题的办法有两个:
-
修改配置文件,让 Tomcat 以 UTF-8 格式 看待/解析 接收到的字节流。
-
不改变配置文件。获得 Tomcat 的 ISO-8859-1 字符串后,生成对应的 UTF-8 字符串,再进行输出打印。
3. 获取 POST 请求中的数据,打印乱码
serverl.xml 中 URIEncoding="xxx"
设置影响不到 post 请求提交来的数据!适用于 get 请求的修改配置方案,对 post 请求无效!
Tomcat 如何 看待/解析 post 请求发送来的数据,取决于 req.setCharacterEncoding("");
。如果未曾调用该方法,Tomcat 以 ISO-8859-1 编码解析接受的post请求数据。同上,随后一旦调用 System.out.println()
就会出现乱码。
解决问题的办法有两个:
-
在调用
req.getParameter()
前调用req.setCharacterEncoding("UTF-8");
, 让 Tomcat 以 UTF-8 格式 看待/解析 接收到的字节流 -
不设置req字符编码。获得 Tomcat 的 ISO-8859-1 字符串后,生成对应的 UTF-8 字符串,再进行输出打印。
如同 get 的方案一对 post 无效,post 的方案一同样解决不了 get 的乱码问题。
但是,显而易见,两者方案二是相同的,这也是后续 过滤器 的主要使用场景。
4. 中文字符串,输出到页面显示乱码
以何种编码将字符转换为字节流后,发送给浏览器,取决于 resp.setCharacterEncoding("xxx");
。没有调用时,默认使用 ISO-8859-1 编码格式。
但是 ISO-8859-1 格式最大的问题在于:它是一个单字节编码,不支持中文。
所以,使用 Tomcat 默认的 ISO-8859-1 编码向浏览器发送的字节流中,如果含有中文信息,浏览器无法正确显示成对应中文。
解决办法只有一个,设置 Tomcat 发送数据的编码格式,并且,resp.setCharacterEncoding()
必须在 PrintWriter out = resp.getWriter();
之前,否则设置无效(因为你已经得到了使用 ISO-8859-1 编码的 out 对象了)。
5. setContentType 和 setCharacterEncoding
在 servletResponse.setCharacterEncoding() 方法的注释中写到了它和 setContentType 之间的关系:
- 如果 response 的字符集已经在 setContentType(或 setLocale)方法中指定,那么你再调用 setCharacterEncoding 方法则会覆盖掉之前的设置。
.setContentType("text/html; charset=UTF-8")
等同于.setContentType("text/html")
+.setCharacterEncoding("UTF-8")
。
另外,在使用 setCharacterEncoding 会出现失效的情况,通常是因为 2 点原因:
- 你在
.getWriter()
之后才调用.setCharacterEncoding()
,这样对字符集的设置太晚,自然是无效的。 - 你有
.setCharacterEncoding()
,但是没有调用.setContentType()
,这样对字符集的设置也是无效的。