JAVA WEB 乱码问题解析
乱码原因
在Java Web开发过程中,经常遇到乱码的问题,造成乱码的原因,概括起来就是对字符编码和解码的方式不匹配。
既然乱码的原因是字符编码与解码的方式不匹配,那么为什么我们一定要对字符进行编码,不编码可不可以呢?这是因为在计算机中存储数据的基本单位是1个字节,即8个bit,那么它所能表达的字符的最多有28=256个,而在我们现实社会中存在的字符(汉字、英文、其他文字等等)远远多余这个数字,所以为了解决字符与字节的矛盾,对字符进行编码处理才能存储在计算机中。
编码与解码
在计算机中常见的编码方式有ASCII、ISO-8859-1、GB2312、UTF-16、UTF-8几种编码方式。
ASCII码是使用一个字节的低7位来表示的,所以共能表达的字符最多有27=128个。ISO-8859-1是ISO组织基于ASCII码的基础上扩展来的,兼容ASCII码,涵盖了大多数西欧字符。ISO8859-1使用一个字节来表示,所以其能表达的字符最多有256个。GB2312,采用了双字节编码,编码范围是A1-F7,其中A1-A9是符号区,B0-F7是汉字区,包含6763个汉字。GBK是为了扩展GB2312编码,并加入了更多的汉字,总能表达的汉字有21003个。UTF-16是采用定长的编码方式,无论什么字符都采用2个字节进行表示,这也是JAVA内存中字符的存储格式。与UTF-16相反,UTF-8采用了变长的编码方式,不同的类型的字符可以由1-6个字节组成。
下面以字符串“日向雏田”来看一下在计算机中不同编码方式的编码,如下图。
乱码分析与解决
对于JAVA WEB中乱码问题,我们划分位请求导致的乱码和响应导致的乱码,对于不同的乱码我们要分析其乱码原因,即字符编码的方式是什么,解码的方式是什么。
对于由于请求导致的乱码我们要分析Http请求,查看其编码方式,由于HTTP请求分为Get请求和Post请求,我们接下来分别对其进行讨论。
对于Get请求,是浏览器默认的请求方式,和表单提交时设置为“Get”时的提交方式。我们通过火狐浏览器我们查看其具体内容如下:
地址栏为:
请求内容为:
通过上面请求我们可以看到,GET请求中查询字符串放在了请求行中存放,发送到WEB服务器中,通过“日向雏田”编码我们可以看到,浏览器对该字符串采用的编码方式为“UTF-8”。
查看服务器代码我们可以看到乱码(如下图),这是因为服务器在接受到该字符串编码后的数据默认通过ISO-8859-1的方式进行解码,所以造成了编码与解码的方式不统一。
解决方案如下:
首先获取字符串user解码前的编码,然后指定该字符串的编码方式,如下图:
解决方案示意图如下:
在Java web开发过程中,我们在超链接中传递参数,经常遇到中文的情况。对此情况下,我们需要对中文进行编码,我们可以设置为UTF-8,解码方案同上。
<a href="${pageContext.request.contextPath}/Test?user=<%=URLEncoder.encode("日向雏田", "UTF-8")%>">点击</a>
对于Post请求,是表单提交时设置为“Post”时的提交方式。我们通过火狐浏览器我们查看其具体内容如下:
地址栏及其页面为:
post请求内容为:
由上图我们可以知道,在post请求中,将请求内容直接放在请求体中发送给web服务器,编码方式为“utf-8”。
在此响应Servlet中,doPost方法体如下:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String user=request.getParameter("user"); System.out.println(user);//输出为æ?¥å??é??ç?° }
此处乱码的原因依然时在代码getParameter(“user”)时,web服务器采用默认的解码方案“ISO-8859-1”进行解码,导致了编码与解码方案的不同意,解决方案可以采用get请求乱码的解决方案,但是还有一种更为简单的解决方案,直接指定方法体的编码/解码方案为“utf-8”。方案如下。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); //设置请求体的编码/解码方案为UTF-8 String user=request.getParameter("user"); System.out.println(user); //输出为日向雏田 }