JavaWeb开发中乱码问题处理

       其实,任何问题都是有理可循的,只要弄明白原理,由原理入手设计解决方案,很多问题也就迎刃而解了。在程序设计中,我们不仅要明白怎么做,更要知道为什么这么做,这才是提升能力的必经之路。
下面,我们来分析一下Javaweb开发中常见的乱码产生的原因及其解决方案。

响应乱码原理

服务器发送数据到浏览器,有字符和字节两种方式,这两种方式产生乱码的原理各不相同。
字节方式输出数据

response.getOutputStream( ).write("中国".getBytes("utf-8"));

发现结果为:

结果显示产生了乱码,那么这个问题是如何产生的呢?
乱码问题,大部分都是由于编码和解码码表不同产生的,服务器在发送中文数据时,使用的是UTF-8码表,那么浏览器是用什么码表解析的呢?IE浏览器在没有特别指定码表时,将使用所在操作系统的默认码表,utf-8的数据发送给浏览器后,浏览器用gbk2312打开自然就出现了乱码问题。

那么如何解决这种乱码呢?在HTTP协议中提供了Content-Type响应头,我们可以通过这个头通知浏览器发送的数据是什么格式,如果是文本数据还可以通过charset属性通知浏览器用什么编码打开,因此我们只要做如下设置即可解决乱码:

response.setHeader("content-type", "text/html;charset=utf-8");

response.getOutputStream().write("中国".getBytes("utf-8"));

还可以使用下面的这种方式进行快速设置:

response.setContentType("text/html;charset=utf-8");

 

字符方式输出数据

如果是通过response.getWriter().write(“中国”)输出数据给浏览器,则服务器会在发送数据时将字符转换为字节后发送,那么服务 器使用的是什么编码集呢?以tomcat6为例,其默认编码集是iso8859-1,我们知道iso8859-1中是没有中文字符的,对于没有的字符 iso8859-1将会使用“?”来替代,所以浏览器页面上就显示成了“?”   tomcat8以后默认为utf-8 

此时想解决这个问题只要让服务器设置为发送数据时使用正确的码表就可以了,两码相同即可消除乱码:

response.setHeader("content-type", "text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
response.getWriter().write("中国");

请求乱码原理

浏览器发送请求时使用的是什么编码呢?浏览器打开表单页面时使用的什么编码就会用什么编码发送数据,我们可以通过设置打开表单页面的编码来控制页面发送数据时的编码。假设表单页面是utf-8,则这个页面发送请求参数用的也是utf-8。

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<h1>GET提交</h1>
<form action="<c:url value='/AServlet'/>" method="get">
国籍:<input type="text" name="country" />
<input type="submit" value="提交" />
</form>
<hr>
<h1>POST提交</h1>
<form action="<c:url value='/AServlet'/>" method="post">
国籍:<input type="text" name="country" />
<input type="submit" value="提交" />
</form>
</body>
</html>

代码中,标黄位置表示表单页面使用utf-8的编码方式编码,查看浏览器:

而服务器是用什么编码解析的呢?以tomcat6为例,服务器默认用iso8859-1来解码,用utf-8编码,这就产生了乱码,因utf-8中是3个字节一个汉字,iso8859-1中是个字节一个字符,如“北京”为两个汉字,在utf-8中共六个字节,而iso8859-1按一个字节一个字符处理,显示出了6个问号。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
String country = req.getParameter("country");
System.out.println(country);
}

我们该如何解决这个问题呢?其实,只要对POST提交做如下设置即可:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
req.setCharacterEncoding("utf-8");
String country = req.getParameter("country");
System.out.println(country);
}

但是,这个方法只对请求的实体内容起作用,因GET提交的请求参数在地址栏后,并不在实体内容中,所以这个方法对于GET提交无效。

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
req.setCharacterEncoding("utf-8");
String country = req.getParameter("country");
System.out.println(country);
}

那么,该如何处理GET提交的乱码呢?
服务器用iso8859-1来解析,造成了乱码,由于iso8859-1无法正确显示这些字符,所以显示为了“?”,但是底层的编码仍然是正确的。所以,我们可以通过如下方式解决:

byte bs = country.getBytes(“iso8859-1”);//将乱码解析回二进制数据
country= new String(bs,”utf-8”);//按照正确的utf-8解码得到正确的字符

简写形式:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
String country = new String(req.getParameter("country").getBytes("iso-8859-1"), "utf-8");
System.out.println(country);
}