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