ASCII,American Standard Code for Information Interchange,是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其余西欧语言。它是现今最通用的单字节编码系统。javascript
ASCII最大的缺点就是显示字符有限,他虽然解决了部分西欧语言的显示问题,可是对更多的其余语言他实在是无能为了。随着计算机技术的发展,使用 范围愈来愈普遍了,ASCII的缺陷愈来愈明显了,其余国家和地区须要使用计算机,必需要设计一套符合本国/本地区的编码规则。例如为了显示中文,咱们就 必需要设计一套编码规则用于将汉字转换为计算机能够接受的数字系统的数。html
GB2312,用于汉字处理、 汉字通讯等系统之间的信息交换,通行于中国大陆。它的编码规则是:小于127的字符的意义与原来相同,但两个大于127的字符连在一块儿时,就表示一个汉 字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样咱们就能够组合出大约7000多个简体汉字了。虽然GB2312收录了这么多汉子,他所覆盖 的使用率能够达到99%,可是对于那些不常见的汉字,例如人名、地名、古汉语,它就不能处理了,因而就有下面的GBK、GB 18030的出现。(点击GB2312简体中文编码表查看)。java
GB18030,全 称:国家标准GB 18030-2005《信息技术 中文编码字符集》,是我国计算机系统必须遵循的基础性标准之一,GB18030有两个版本:GB18030-2000和GB18030-2005。 GB18030-2000是GBK的取代版本,它的主要特色是在GBK基础上增长了CJK统一汉字扩充A的汉字。程序员
GB 18030主要有如下特色:web
与UTF-8相同,采用多字节编码,每一个字能够由1个、2个或4个字节组成。chrome
编码空间庞大,最多可定义161万个字符。数据库
支持中国国内少数民族的文字,不须要动用造字区。apache
GBK,汉字编码标准之一,全称《汉字内码扩展规范》,它 向下与 GB 2312 编码兼容,向上支持 ISO 10646.1 国际标准,是前者向后者过渡过程当中的一个承上启下的标准。它的编码范围以下图:浏览器
正如前面前面所提到的同样,世界存在这么多国家,也存在着多种编码风格,像中文的GB23二、GBK、GB18030,这样乱搞一套,虽然在本地运行没有问题,可是一旦出如今网络上,因为互不兼容,访问则会出现乱码。为了解决这个问题,伟大的Unicode编码腾空出世。tomcat
Unicode编码的做用就是可以使计算机实现夸平台、跨语言的文本转换和处理。它几乎包含了世界上全部的符号,而且每一个符号都是独一无二的。在它的编码世界里,每个数字表明一个符号,每个符号表明了一个数字,不存在二义性。
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其余实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍:UTF-8是Unicode的实现方式之一。
经过程序咱们能够看到“我是 cm”的结果为:
char[]:6211 662f 20 63 6d
ISO-8859-1:3F 3F 20 63 6D
GBK:CE D2 CA C7 20 63 6D
UTF-8:E6 88 91 E6 98 AF 20 63 6D
客户端想服务器发送请求无非就经过四中状况:
一、URL方式直接访问。
二、页面连接。
三、表单get提交
能够看到各大浏览器对“我是”的编码状况以下:
path部分 |
Query String |
|
Firefox |
E6 88 91 E6 98 AF |
E6 88 91 E6 98 AF |
Chrome |
E6 88 91 E6 98 AF |
E6 88 91 E6 98 AF |
IE |
E6 88 91 E6 98 AF |
CE D2 CA C7 |
查阅上篇博客的编码可知对于path部分Firefox、chrome、IE都是采用UTF-8编码格式,对于Query String部分Firefox、chrome采用UTF-8,IE采用GBK。至于为何会加上%,这是由于URL的编码规范规定浏览器将ASCII字 符非 ASCII 字符按照某种编码格式编码成 16 进制数字而后将每一个 16 进制表示的字节前加上“%”。
固然对于不一样的浏览器,相同浏览器不一样版本,不一样的操做系统等环境都会致使编码结果不一样,上表某一种状况,对于URL编码规则下任何结论都是过早 的。因为各大浏览器、各个操做系统对URL的URI、QueryString编码均可能存在不一样,这样对服务器的解码势必会形成很大的困扰,下面咱们将已 tomcat,看tomcat是如何对URL进行解码操做的。
解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:
protected void convertURI(MessageBytes uri, Request request) throws Exception { ByteChunk bc = uri.getByteChunk(); int length = bc.getLength(); CharChunk cc = uri.getCharChunk(); cc.allocate(length, -1); String enc = connector.getURIEncoding(); //获取URI解码集 if (enc != null) { B2CConverter conv = request.getURIConverter(); try { if (conv == null) { conv = new B2CConverter(enc); request.setURIConverter(conv); } } catch (IOException e) {...} if (conv != null) { try { conv.convert(bc, cc, cc.getBuffer().length - cc.getEnd()); uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength()); return; } catch (IOException e) {...} } } // Default encoding: fast conversion byte[] bbuf = bc.getBuffer(); char[] cbuf = cc.getBuffer(); int start = bc.getStart(); for (int i = 0; i < length; i++) { cbuf[i] = (char) (bbuf[i + start] & 0xff); } uri.setChars(cbuf, 0, length); }
从上面的代码可知,对URI的解码操做是首先获取Connector的解码集,该配置在server.xml中
<Connector URIEncoding="utf-8" />
若是没有定义则会采用默认编码ISO-8859-1来解析。
对于Query String部分,咱们知道不管咱们是经过get方式仍是POST方式提交,全部的参数都是保存在Parameters,而后咱们经过 request.getParameter,解码工做就是在第一次调用getParameter方法时进行的。在getParameter方法内部它调用 org.apache.catalina.connector.Request 的 parseParameters 方法,这个方法将会对传递的参数进行解码。下面代码只是parseParameters方法的一部分:
//获取编码 String enc = getCharacterEncoding(); //获取ContentType 中定义的 Charset boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); if (enc != null) { //若是设置编码不为空,则设置编码为enc parameters.setEncoding(enc); if (useBodyEncodingForURI) { //若是设置了Chartset,则设置queryString的解码为ChartSet parameters.setQueryStringEncoding(enc); } } else { //设置默认解码方式 parameters.setEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); if (useBodyEncodingForURI) { parameters.setQueryStringEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); } }
从上面代码能够看出对query String的解码格式要么采用设置的ChartSet要么采用默认的解码格式ISO-8859-1。注意这个设置的ChartSet是在 http Header中定义的ContentType,同时若是咱们须要改指定属性生效,还须要进行以下配置:
<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>
上面部分详细介绍了URL方式请求的编码解码过程。其实对于咱们而言,咱们更多的方式是经过表单的形式来提交。
咱们知道经过URL方式提交数据是很容易产生乱码问题的,因此咱们更加倾向于经过表单形式。当用户点击submit提交表单时,浏览器会更加设定的 编码来编码数据传递给服务器。经过GET方式提交的数据都是拼接在URL后面(能够当作query String??)来提交的,因此tomcat服务器在进行解码过程当中URIEncoding就起到做用了。tomcat服务器会根据设置的 URIEncoding来进行解码,若是没有设置则会使用默认的ISO-8859-1来解码。假如咱们在页面将编码设置为UTF-8,而 URIEncoding设置的不是或者没有设置,那么服务器进行解码时就会产生乱码。这个时候咱们通常能够经过new String(request.getParameter(“name”).getBytes(“iso-8859-1″),”utf-8″) 的形式来获取正确数据。
咱们知道JSP页面是须要转换为servlet的,在转换过程当中确定是要进行编码的。在JSP转换为servlet过程当中下面一段代码起到相当重要的做用。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="GBK" %>
在上面代码中有两个地方存在编码:pageEncoding、contentType的charset。其中pageEncoding是jsp文件自己的编码,而contentType的charset是指服务器发送给客户端时的内容编码。
在前面一篇博客中就提到过(java中文乱码解决之道(四)—–java编码转换过程)jsp在转换为Servlet的过程当中是须要通过主要的三次编码转换过程(除去数据库编码转换、页面参数输入编码转换):
第一次:转换为.java文件;
第二次:转换为.class文件;
第三次:业务逻辑处理后输出。
第一阶段
JVM将JSP编译为.jsp文件。在这个过程当中pageEncoding就起到做用了,JVM首先会获取pageEncoding的值,若是该值存在则采用它设定的编码来编译,不然则采用file.encoding编码来编译。
第二阶段
JVM将.java文件转换为.class文件。在这个过程就与任何编码的设置都没有关系了,无论JSP采用了什么样的编码格式都将无效。通过这个阶段后.jsp文件就转换成了统一的Unicode格式的.class文件了。
第三阶段
流程如以下:
咱们主要经过两种形式提交向服务器发送请求:URL、表单。而表单形式通常都不会出现乱码问题,乱码问题主要是在URL上面。经过前面几篇博客的介 绍咱们知道URL向服务器发送请求编码过程实在是实在太混乱了。不一样的操做系统、不一样的浏览器、不一样的网页字符集,将致使彻底不一样的编码结果。若是程序员 要把每一种结果都考虑进去,是否是太恐怖了?有没有办法,可以保证客户端只用一种编码方法向服务器发出请求?
使用javascript编码不给浏览器插手的机会,编码以后再向服务器发送请求,而后在服务器中解码。在掌握该方法的时候,咱们须要料及javascript编码的三个方法:escape()、encodeURI()、encodeURIComponent()。
采用SIO Latin字符集对指定的字符串进行编码。全部非ASCII字符都会被编码为%xx格式的字符串,其中xx表示该字符在字符集中所对应的16进制数字。例如,格式对应的编码为%20。它对应的解码方法为unescape()。
注意,escape()不对”+”编码。可是咱们知道,网页在提交表单的时候,若是有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。因此,使用的时候要当心。
对整个URL进行编码,它采用的是UTF-8格式输出编码后的字符串。不过encodeURI除了ASCII编码外对于一些特殊的字符也不会进行编码如:! @ # $& * ( ) = : / ; ? + ‘。
把URI字符串采用UTF-8编码格式转化成escape格式的字符串。相对于encodeURI,encodeURIComponent会更增强 大,它会对那些在encodeURI()中不被编码的符号(; / ? : @ & = + $ , #)通通会被编码。可是encodeURIComponent只会对URL的组成部分进行个别编码,而不用于对整个URL进行编码。对应解码函数方法 decodeURIComponent。
固然咱们通常都是使用encodeURI方来进行编码操做。所谓的javascript两次编码后台两次解码就是使用该方法。javascript解决该问题有一次转码、两次转码两种解决方法。
javascript转码:
var url = '<s:property value="webPath" />/ShowMoblieQRCode.servlet?name=我是cm'; window.location.href = encodeURI(url);
转码后的URL:http://127.0.0.1:8080/perbank/ShowMoblieQRCode.servlet?name=%E6%88%91%E6%98%AFcm
后台处理:
String name = request.getParameter("name");
System.out.println("前台传入参数:" + name); name = new String(name.getBytes("ISO-8859-1"),"UTF-8"); System.out.println("通过解码后参数:" + name);
输出结果:
前台传入参数:??????cm
通过解码后参数:我是cm
javascript
var url = '<s:property value="webPath" />/ShowMoblieQRCode.servlet?name=我是cm'; window.location.href = encodeURI(encodeURI(url));
转码后的url:http://127.0.0.1:8080/perbank/ShowMoblieQRCode.servlet?name=%25E6%2588%2591%25E6%2598%25AFcm
后台处理:
String name = request.getParameter("name");
System.out.println("前台传入参数:" + name); name = URLDecoder.decode(name,"UTF-8"); System.out.println("通过解码后参数:" + name);
输出结果:
前台传入参数:E68891E698AFcm
通过解码后参数:我是cm
使用过滤器,过滤器LZ提供两种,第一种设置编码,第二种直接在过滤器中进行解码操做。
该过滤器是直接设置request的编码格式的。
public class CharacterEncoding implements Filter { private FilterConfig config ; String encoding = null; public void destroy() { config = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(encoding); chain.doFilter(request, response); } public void init(FilterConfig config) throws ServletException { this.config = config; //获取配置参数 String str = config.getInitParameter("encoding"); if(str!=null){ encoding = str; } } }
配置:
<!-- 中文过滤器的配置 --> <filter> <filter-name>chineseEncoding</filter-name> <filter-class>com.test.filter.CharacterEncoding</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>chineseEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
public class CharacterEncoding implements Filter { protected FilterConfig filterConfig ; String encoding = null; public void destroy() { this.filterConfig = null; } /** * 初始化 */ public void init(FilterConfig filterConfig) { this.filterConfig = filterConfig; } /** * 将 inStr 转为 UTF-8 的编码形式 * * @param inStr 输入字符串 * @return UTF - 8 的编码形式的字符串 * @throws UnsupportedEncodingException */ private String toUTF(String inStr) throws UnsupportedEncodingException { String outStr = ""; if (inStr != null) { outStr = new String(inStr.getBytes("iso-8859-1"), "UTF-8"); } return outStr; } /** * 中文乱码过滤处理 */ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 得到请求的方式 (1.post or 2.get), 根据不一样请求方式进行不一样处理 String method = request.getMethod(); // 1. 以 post 方式提交的请求 , 直接设置编码为 UTF-8 if (method.equalsIgnoreCase("post")) { try { request.setCharacterEncoding("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } // 2. 以 get 方式提交的请求 else { // 取出客户提交的参数集 Enumeration<String> paramNames = request.getParameterNames(); // 遍历参数集取出每一个参数的名称及值 while (paramNames.hasMoreElements()) { String name = paramNames.nextElement(); // 取出参数名称 String values[] = request.getParameterValues(name); // 根据参数名称取出其值 // 若是参数值集不为空 if (values != null) { // 遍历参数值集 for (int i = 0; i < values.length; i++) { try { // 回圈依次将每一个值调用 toUTF(values[i]) 方法转换参数值的字元编码 String vlustr = toUTF(values[i]); values[i] = vlustr; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } // 将该值以属性的形式藏在 request request.setAttribute(name, values); } } } // 设置响应方式和支持中文的字元集 response.setContentType("text/html;charset=UTF-8"); // 继续执行下一个 filter, 无一下个 filter 则执行请求 chain.doFilter(request, response); } }
配置:
<!-- 中文过滤器的配置 --> <filter> <filter-name>chineseEncoding</filter-name> <filter-class>com.test.filter.CharacterEncoding</filter-class> </filter> <filter-mapping> <filter-name>chineseEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
一、设置pageEncoding、contentType
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
二、设置tomcat的URIEncoding