首页 > 代码库 > web乱码解决

web乱码解决

前言: 我之前对于乱码的态度就是上网上搜解决办法,找到一个就实验一个,直到找到了能解决的办法就行了,也不去仔细研究,结果就是每次出现乱码,我都不知怎么解决,有重复以前的步骤,

找到一个实验一个。 这样的学习态度是很不可取的,不仅学不到东西,而且极大的降低了自己的学习热情,现在我要彻底解决它。

编码的基础知识

编码和解码: 首先明确码是什么? 码是字节数组,所以编码(encode)就是从字符串得到字节数组,而解码(decode)就是从字节数组得到字符串,在编码和解码过程中需要一个标准,其实就是

字节与字符之间的对应关系,这个就是字符集。在java中String类有两个常用的方法进行编码和解码

getBytes :例如“中”.getBytes(“字符集”),根据指定的字符集进行编码

String(bytes[],”字符集”):根据自定的字符集对字节数组进行解码

另外还有一个概念是为URI编码和URI解码,不过URI编码和解码不是字符串和字节流之间的转换,而是由一个字符串表示另一个字符串,例如:

“中”的UTF-8 URI编码为 %E4%B8%AD

字符串%E4%B8%AD根据UTF-8进行URI解码为字符“中”

可以看出来,URI编码就是将一个字符串用%+对应字符集的编码组织的字符串来表示的,java中uri编码和uri解码的方法

URLEncoder.encode("傻逼", "utf-8")

URLDecoder.decode("%e5%82%bb%e9%80%bc", "gbk");

另外在js中用于uri编码的常用方法是:encodeURIComponent() 函数 与 encodeURI() 函数,关于他们的区别参见另一篇笔记。

 

出现乱码的原因:

web是基于请求响应的,当我们通过浏览器向服务器发送请求时会携带请求参数,而参数是需要转换为字节流来在网络上传播的,也就是编码,所以浏览器就

会根据一定的原则来确定根据某个字符集来对请求参数编码,当请求参数传递到服务器后,服务器又会根据某种原则选择出响应的字符集对浏览器传递来的字节流

进行解码为字符串。了解了这个过程我们就发现问题了,如果这两次编码和解码所依据的字符集不同,那么自然就会出现乱码了,这也就是乱码出现的本质。

首先来看看浏览器是怎样来编码的:

向服务器提交请求一般有三种方式: 1.提交表单 2.超链接 3.ajax

1. 提交表单 又分为post和get两种方式

采用post方法时浏览器会将表单中的字符串,采用页面的字符集编码为字节流发送到服务器,

采用get方法时,浏览器首先会将表单中的值通过页面的字符集行URI编码后拼接到action的URL后面发送到应用服务器

所以可以知道无论post还是get,编码方式都是有页面上指定的字符集确定的。

2.ajax

这个是比较蛋疼的,因为页面上设置的编码方式对于ajax不起作用,我在做光纤地图的项目中就吃了亏,而是总是按照utf-8来编码的

3.超链接

对于超链接,如果携带中文,浏览器会进行uri编码,但是编码方式和客户端的环境有关。所以为了避免浏览器进行不确定的URI编码,需要在程序中将中文进行URI编码后在放到URL中。

下面就是看服务器端的解码方式了:

下面是针对tomcat而言:

对于post方式提交(包括表单和ajax 的post),编码方式是由request.setCharacterEncoding()方法来决定的,在servlet中应该在getParameter方法之前调用这个方法才有作用。

在struts中是通过<constant name="struts.i18n.encoding" value=http://www.mamicode.com/"gbk"></constant>这个常量来设置的。

对于get和超连接的情况,下面是参考别人的说法:

tomcat5.5中getParameter获取get方法或超链接传过来的参数时默认会用ISO8859-1进行解码,例如浏览器发送UTF-8的编码的请求,tomcat5.5的getParameter使用ISO8859-1解码,这时的结果是错的,如果要获得正确的值,需要在tomcat5.5的getParameter的时候采用UTF-8进行解码,通过设置URIEncoding="UTF-8"或者useBodyEncodingForURI="true",就能让tomcat在的getParameter时采用UTF-8解码(useBodyEncodingForURI="true"表示解码的字符集采用与页面编码相同的字符集),对于超链接的情况,在服务器端还应该对开始uri编码的参数进行uri解码。

 

通过上面的分析,我们可以发现只要编码和解码的字符集设置为一样的,就不会出现乱码,所以总结为:

1. 设置请求页面的编码,pageEncoding="GBK"

2.对于超连接采用encodeURI("中国","GBK")来进行uri转码。

3.在服务器端设置request.setCharacterEncoding()或者<constant name="struts.i18n.encoding" value=http://www.mamicode.com/"gbk"></constant>

4.在tomcat中设置URIEncoding="UTF-8"或者useBodyEncodingForURI="true"

 

到此为止,我们还剩下一个问题就是ajax时的编码问题,如果我们采用的统一编码为utf-8,那么ajax是没有什么问题的,但是如果统一编码为gbk,就有问题了,因为ajax在浏览器端总是

通过utf-8进行编码的,而在服务器端我们设置的统一编码为gbk,自然就出现乱码了,其实我们可以分析一下就是 

字符串---》uft-8编码---》gbk解码 造成的乱码,我们先看看这种思路:可以逆向转换回去,首先使用gbk编码,然后在使用utf-8解码,就比如

        byte[] buf = "中国".getBytes("utf-8");//使用uft-8编码        String str1 = new String(buf, "gbk");//使用gbk解码        System.out.println(str1);//打印出乱码        byte[] buf2 = str1.getBytes("gbk");//使用gbk编码        String str2 = new String(buf2, "utf-8");//使用utf-8解码        System.out.println(str2);//打印出中国

 这种方法看似很合理,其实是错误的 因为如果出现乱码了,未必能由乱码字符串编码会原来的字节流,比如上面的例子,如果不是“中国”,而是“中”,或者“中国人”,就不能得到正确的结果。

所以正在可行的就是下面这种做法,

把中文字符串uri编码先变为ascii字符串,这样无论使用何种编码字符集都可以再服务器端得到这个新生成的ascii字符串,然后自己在对这个ascii字符串按照在开始时uri编码的

字符集来uri解码,就可以保证得到正确的中文参数了。

还有一种解决办法就是想对待超连接一样,在客户端发送之前对参数进行一次uri编码,让参数成为ascii字符串,这样

就可以利用任何字符集编码解码ascii字符串得到相同结果的原理,当服务器端使用不同字符集解码后仍然得到原参数uri编码得到的那个ascii字符串, 最后自己在进行一次uri解码就可以了。

        String a = URLEncoder.encode("说说","utf-8");//模拟在客户端发送之前自己先编码一次,将参数变为ascii字符串                byte[] buf = a.getBytes("utf-8");;//模拟浏览器对参数编码        String b = new String(buf, "gbk");//模拟服务器对参数解码                String c = URLDecoder.decode(b, "utf-8");//最后自己在把参数解码                System.out.println(c);//打印 “说说”

 

最后说一下为什么我们无须使用spring的编码过滤器,在spring编码过滤器中有:

public void prepare(HttpServletRequest request, HttpServletResponse response) {          String encoding = null;          if (defaultEncoding != null) {              encoding = defaultEncoding;          }           //省略了一些代码            if (encoding != null) {              try {                  request.setCharacterEncoding(encoding);//设置了字符集编码              } catch (Exception e) {                  LOG.error("Error setting character encoding to ‘" + encoding + "‘ - ignoring.", e);              }          }      //省略了一些代码      }  

我们可以看到其实它不过也就是调用了

request.setCharacterEncoding(encoding);而已,和我们自己做的没有什么太大区别。

设置浏览器的页面编码

服务器向浏览器发送的也是经过编码成字节流在网络上传输,浏览器接收到字节流之后使用指定的字符集解码成字符串再进行展现,如果这两个环节的字符集不一致也会导致乱码的问题,

例如静态HTML文件或jsp中都是以UTF-8保存的,则需要告诉浏览器用UTF-8来进行解码,

 

  • 如果是jsp可以通过<%@ page contentType="text/html; charset=UTF-8" language="java" %>来进行设置,
  • 静态文件可以通过 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 进行设置,
  • 如果在servlet中直接进行输出,可以通过response.setCharacterEncoding("UTF-8"),setContentType("text/html;charset=UTF-8"),setHeader("Content-Type","text/html;charset=UTF-8")进行设置,

 

这些操作都相当于在response的头部增加"Content-Type:text/html;charset=UTF-8"信息,

header中的编码信息的优先级要高于html的meta标签,也就是说如果serlvet中设置了setContentType("text/html;charset=UTF-8"),jsp设置了<meta http-equiv="Content-Type" content="text/html; charset=GBK"/>则浏览器会按照UTF-8字符集进行解码,

 

web乱码解决