javaweb中的亂碼問題

0.爲何須要編碼,解碼,

       不管是圖片,文檔,聲音,在網絡IO,磁盤io中都是以字節流的方式存在及傳遞的,可是咱們拿到字節流怎麼解析呢?這句話就涉及了編碼,解碼兩個過程,從字符數據轉化爲字節數據就是編碼,從字節數據轉化爲字符數據是解碼,可能有人疑問,一個字符不是一個字節,兩個字節嗎?一堆字符不就是一堆字節嗎,須要轉什麼?好,剛纔所說 的以及涉及到編碼了,有的編碼是一個字節一個字符,就像ASCII碼,可是漢字以及其餘語言文字太多,很明顯一個字節不能表示全部字符,因此纔會引伸出如 此多的編碼,如今主要討論ISO-8859-1,utf-8,gbk ,其中iso-8859編碼是沒法對中文進行編碼的css


        String ISO = "ISO-8859-1";
        String UTF = "UTF-8";
        String GBK = "GBK";
        String string = "很開心分享經驗";
        byte[] bytes = string.getBytes(ISO);
        
        System.out.println("結果:"+new String(bytes,ISO));
        for(byte b:bytes){
            System.out.print(b+" ");
        }html

 

 

ISO只要遇到不認識的字符,都會將其用63表示,顯示出來,也就是 ?  全部的都變成了問號 這裏能夠看到六個中文對應6個 ?java

順便說一下,英文不會涉及iso,utf-8,gbk,編碼問題,由於英文能夠用ASCII碼錶示,這幾種編碼對ASCII碼兼容mysql

String string = "很開心分享經驗  happy the world";linux

 

 

32表明 空格ios

utf-8編解碼

String ISO = "ISO-8859-1";
        String UTF = "UTF-8";
        String GBK = "GBK";
        String string = "很開心分享經驗";
        byte[] bytes = string.getBytes(UTF);
        
        System.out.println("結果:"+new String(bytes,UTF));
        for(byte b:bytes){
            System.out.print(b+" ");
        }web

 

 

能夠看到 編碼沒有問題,並且每一個漢字佔用三個字節 ,一共21個(UTF-16一共佔用16個)sql

GBK編碼

代碼省略了數據庫

 

 

編解碼仍是沒有問題, 可是每一個漢字佔用 兩個字節,一共14個windows

咱們知道了,只要遇到一大堆??????這樣的亂碼,通常能夠確認,有一個地方用到了iso的編碼,這是中文不能使用的編碼除此以外,咱們還會看到不少種其餘的亂碼例如:

  1. 1.   寰堝紑蹇冨垎浜粡楠�

2. 2.   �ܿ��ķ��?�� 

3.  ºÜ¿ªÐÄ·ÖÏí¾­Ñé  

4. 很开心分享经骠

這四種亂碼,好像都不同哦,各有各的風采,然而並看不懂。

1.   寰堝紑蹇冨垎浜粡楠�    UTF-GBK

2.   �ܿ��ķ��?��               GBK-UTF

3.  ºÜ¿ªÐÄ·ÖÏí¾­Ñé                     GBK-ISO

4. 很开心分享经骠                 UTF-ISO

(5 襉벀菥褘ꯧ뮏�    utf-8 ---utf-16

這四種分別對應,不一樣的編碼-解碼  例如第一個  很開心分享經驗  用utf-8編碼後,gbk解碼後 就變成了這坨  寰堝紑蹇冨垎浜粡楠�  ,剩下三坨很少說

如今總結下(一次編解碼):

1. 只要是iso對中文字符編碼就必定是一大堆?????,爲何呢,由於咱們說了iso把不認識的都轉成63     63是什麼在ASCII碼中是? 而這些編碼基本都兼容ASCII,因此

只要是一大坨???????就必定有一個地方採用的是iso編碼,然後面還會說道iso編碼是不少地方的默認編碼(默認的爲何不是utf-8,,鬱悶!!!)

2  當咱們看到亂碼以後,不要慌,第一步先不要想在哪裏出現編碼問題,先考慮  多是哪種編解碼 錯誤。而這種錯誤是有章可循的。上邊內四個基本差很少,(若是還有發現別的,我會再加上)

 

那麼若是出現其餘好屢次編解碼呢?  那這個問題就複雜的多了

1  .String ISO = "ISO-8859-1";
        String UTF = "UTF-8";
        String GBK = "GBK";
        String string = "很開心分享經驗";
        byte[] bytes = string.getBytes(UTF);
        String string2 = new String(bytes,ISO);
        byte[] bytes2 = string2.getBytes(ISO);
        String string3 = new String(bytes2,UTF);

        System.out.println("結果:"+string3);

 

 

UTF編碼,iso解碼,ios解碼,utf-8解碼  中間經歷了曲折,可是,最終由變成了 中文,好艱難

可是咱們應該爲此慶幸嗎?  我以爲不能,你最好也這麼以爲,全部的編解碼,要統一

統一以前,咱們也應該明白,咱們總會有疏漏的地方,萬一一不留神呢,,因此再看看其餘的混合編解碼

2.String string = "很開心分享經驗";
        byte[] bytes = string.getBytes(UTF);
        String string2 = new String(bytes,ISO);
        byte[] bytes2 = string2.getBytes(UTF);
        String string3 = new String(bytes2,ISO);

        System.out.println("結果:"+string3);

這一次咱們 進行了兩次 utf編碼,ios解碼, 可是結果什麼樣子呢

 

翻翻前面的,基本差很少,只不太小寫變大寫,最重要的是長度增長了一倍

3.

     byte[] bytes = string.getBytes(UTF);
        String string2 = new String(bytes,GBK);
        byte[] bytes2 = string2.getBytes(UTF);
        String string3 = new String(bytes2,GBK);

        System.out.println("結果:"+bytes2.length+string3);

這一次咱們進行了 一個utf-8,編碼,gbk解碼,utf-8編碼,gbk解碼,依然是亂碼,長度又增長了 二分之一(utf-8表示一箇中文三個字節,gbk是兩個字節)

 

4.byte[] bytes = string.getBytes(UTF);
        String string2 = new String(bytes,GBK);
        byte[] bytes2 = string2.getBytes(GBK);
        String string3 = new String(bytes2,UTF);

        System.out.println("結果:"+string3);

此次是utf-8編碼,gbk解碼,gbk編碼,utf-8解碼。最終的結果好玩

 

 

一部分中文被正確顯示,另一些漢字慘遭拋棄。。。

5.byte[] bytes = string.getBytes(GBK);
        String string2 = new String(bytes,UTF);
        byte[] bytes2 = string2.getBytes(UTF);
        String string3 = new String(bytes2,GBK);

        System.out.println("結果:"+string3);

這一次是GBK編碼,utf-8解碼,utf-8編碼,gbk解碼

 

六、

byte[] bytes = string.getBytes(GBK);
        String string2 = new String(bytes,UTF);
        byte[] bytes2 = string2.getBytes(GBK);
        String string3 = new String(bytes2,GBK);

        System.out.println("結果:"+string3);

此次咱們gbk編碼,utf-8解碼,gbk編碼,gbk解碼,

獲得的結果感人::

 

 

一大坨 ??? 剛纔我說的都是一大坨 ???編碼確定是iso,可是得有前提,長度相同

到這裏,不能在總結了,意思很明確,通過一次兩次編碼,結果可能已經面目全非,可是仔細分析,每一種狀況的具體結果仍是不一樣的,對於咱們不少人來講,要想記住這些全部的亂碼狀況,是不可能的,可是若是咱們真的遇到了一些屢次編解碼,多種編碼方式,出現的亂碼時,有過這方面的試驗經驗,或許能夠解決的更快一些。

下面從請求處理的流程,以及具體流程的某個階段可能出現的亂碼問題

1.http請求的編碼

提交表單(通常設置爲method = POST ,一下說的表單默認是post),或者在地址欄直接輸入url地址(get請求)

首先先說get方式,也就是輸入地址欄 的url

1.http://localhost:8080/TestCharSet/test?charset=中文

紅色部分爲pathinfo,也就是路徑部分,綠色部分是QueryInfo部分,也就是查詢字符串,在後臺咱們通常這樣獲取

String charset = request.getParameter(「charset」);

以上我在地址欄中輸入的,當我從 地址欄複製到word中時,他轉成了這個

http://localhost:8080/TestCharSet/test?charset=%E4%B8%AD%E6%96%87

 後面的%加上數字字母都是URL編碼 加上%是由於16進製表示,因此在前面加個%

URL編碼的過程很簡單,以下:

  1. 將待編碼字符原先的存儲編碼當作一個16進制流【將原2進制流按 字節拆分,每一個字節都用2位16進制數表示】;
  2. 在每兩位16進制數(即一個完整的字節)前加一個%,獲得最終編碼結果;

        對於漢字來講,首先要看其自己存儲時所使用的編碼是UTF-8仍是GB2312。一樣的漢字,存儲編碼不一樣,經URL編碼後的結果天然也不一樣。例如「川」,使用UTF-8編碼存儲時爲 e5b79d ,經URL編碼後則爲 %e5%b7%9d ;使用GB2312編碼存儲時爲 b4a8 ,經URL編碼後則爲 %b4%a8 。

 

 

        解碼的時候也很簡單,將編碼裏的%號去掉,獲得一個16進制流,這個16進制流轉回2進制流,獲得的就是原字符的存儲編碼。剩下的一個重要問題是怎麼理解這個還原出來的存儲編碼(即原字符使用的存儲編碼方式)?分三種狀況:

  • 對於HTTP請求正文中的URL編碼,咱們能夠查看請求頭部中 Charset 頭域的值,它指定了請求報文所使用的字符集(即存儲編碼方式)。如圖1,由於 Charset 的值爲UTF-8,因此咱們對解碼後的結果就應當按UTF-8編碼理解了;
  • 由於使用UTF-8編碼時,一個漢字的自己存儲佔三個字節;而使用GB2312編碼,一個漢字的自己存儲佔兩個字節。所以若是咱們能肯定被編碼的是純漢字流的話,咱們能夠根據解碼後的結果佔用的字節數是3或者2的倍數來大體推斷其存儲編碼方式; 
  • 上述方法都不行的話,就只能在譯碼的時候都試一下了

 具體的url編碼請查看連接 http://www.tuicool.com/articles/3mUNFz 這裏寫的很好

 

前面說到了 瀏覽器把一個具體編碼的字符序列按照url編碼 爲16進制,一樣服務器端,按照url解碼,將url進行解碼 獲得了具體的字符序列,那麼服務器拿到了這個字符序列怎麼辦呢,就能夠讀取了嗎,不能,咱們還須要知道這個字符序列是什麼編碼方式,不然咱們根本正常讀取不了(不然就會亂碼)

 

2)post方式的編碼

 例如表單POST請求,會將提交的參數放到請求主體部分

 

 

例如name就被放在主體部分,一樣,它也是瀏覽器經過url編碼(16進制編碼)把數據傳送到服務器端,具體的服務器拿到這個16進制解碼的結果,也就是這個字符序列,如何處理,是按照什麼方式解析呢,一樣是 制定的charset值,可是通常表單提交後,服務器端須要咱們指定具體的charset進行解碼,具體的方式

你們都用過:request.setCharacterEncoding(charset);

 

三、應用服務器如何解析參數

   咱們知道url也就是get請求的路徑部分,須要解碼,解碼的方式是charset指定的方式,可是具體的,可能頭文件中並無指定charset,這時 應用服務器會採用默認的編碼方式

具體到tomcat中

 <Connector URIEncoding="UTF-8" port="8080" protocol="HTTP/1.1"

               connectionTimeout="20000"

               redirectPort="8443" />

這個uriencoding屬性能夠設置tomcat的解析的uri的編碼

若是不設置,默認是IOS-8859-1,uri中存在中文字符,即便設置了request.setCharaeterEncoding(「utf-8」)在request中也解析不出來的。

以下

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

                            throws ServletException, IOException {

                   System.out.println("GET請求處理");

                   req.setCharacterEncoding("utf-8");//在這裏設置了utf-8

                   String string = req.getParameter("charset");

                   if(string!=null){

                            System.out.println(string);

                   }else {

                            System.out.println("沒有獲取到變量");

                   }

                  

                   PrintWriter pw = resp.getWriter();

                   pw.println(req.getRequestURI()+"請求成功");

                   pw.close();

         }

請求路徑中包含中文

http://localhost:8080/TestCharSet/test?charset=中文

 

 

 

若是設置了URIEncoding="UTF-8"以後,即便沒有設置reqest.setCharaeterEncoding,也能夠獲取到參數,可是對於post請求,沒法獲取到name

 

 

 

當設置了reqest.setCharaeterEncoding(utf-8)後,post請求能夠獲取到

 

 

 

咱們能夠總結一下,也就是說url中的編碼必須統一uriEncoding設置,能夠不設置reqest.setCharaeterEncoding(utf-8)

可是若是遇到post請求,參數信息在請求主體中,那咱們必須設置

reqest.setCharaeterEncoding(utf-8)這樣才能保證獲取到正確的參數信息。

 

 

這裏多提一點,tomcat在對請求頭文件解析的時候,默認是先不解析請求主體字符串,由於字符串操做很是耗費性能,tomcat把解析工做延遲到第一次調用 req.getParameter中進行,以後便將全部的參數所有注入到tomcat中的一個ParameterMap數據結構中,在解析以後,你即便經過req.setCharaeter也沒有用了。

(在由byte[]流轉化爲Java中的String時,須要指定編碼,這個編碼就是經過request設置的)

Tomcat還有一個參數useBodyEncodingForURI,這個參數是什麼意思呢,當它爲false時,對url中的編碼採用tomcat默認的或者uriEncoding設置的編碼方式。當爲true時,get請求中的查詢字符串按照http請求主體中的編碼方式解碼,而請求主體的解碼方式,咱們經過

request.setCharaeterEncoding設置,而瀏覽器發送請求主體的編碼方式是發送post,get請求的本界面的charset進行編碼。後面會具體談到瀏覽器發送http請求的編碼

 

不光request,response也須要字符轉化,看個例子

這時處理get請求的servlet代碼,像屏幕打印輸出請求連接,以及「請求成功」

PrintWriter pw = resp.getWriter();

pw.println(req.getRequestURI()+"請求成功");

pw.close();

 

 

 

 

能夠看到請求成功是亂碼,由於response將」請求成功「轉化爲 byte[]流時,默認採用的

ISO-8859-1,因此打印的都是????

 

基於此reponse也要設置字符編碼,response.setCharaeterEncoding(「utf-8」)

 

 

這時顯示正常了

可是可能有的小夥伴顯示的結果多是這個

 

 

仍是亂碼,往上面找咱們總結的四種狀況,最接近第一種,也就是說用utf-8編碼,gbk解碼的狀況,事實也是如此,瀏覽器接收到byte[]流後,按照的是GBK解碼,我用的是火狐瀏覽器,在文字編碼設置中,將簡體中文改爲Unicode後,即正常顯示。

或者咱們採用一種更加優雅的方式

pw.println("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">");

打印meta標籤,這樣即便咱們手動指定文字編碼爲gbk(簡體中文),瀏覽器接收到這個標籤後,會默認按照這個標籤中指定的編碼,進行解碼顯示。一樣,meta標籤指定的編碼也要和response.setXXX方法設置的同樣,不然也會亂碼

 

再繼續深刻一下,這是經過response打印輸出,若是直接跳轉到jsp頁面呢?首先咱們先不設置 response的編碼集也就是默認的轉到Charser.jsp界面該界面以下

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%

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></title>

    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />

         <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>copyright @ 青銅器工做室  </h1>

  </body>

</html>

 

在這個界面裏,有一段中文,看看這個界面能不能正常顯示

req.getRequestDispatcher("/Charset.jsp").forward(req, resp);

這個將請求轉發到Charset.jsp這個界面

 

 

 

結果如圖,response在沒有設置編碼狀況下,仍是將jsp中的中文按照iso進行編碼,變成了???

有什麼方式能夠避免這種狀況發生呢

事實上咱們能夠看到

即便加上<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

若是不用response設置默認編碼,一樣會亂碼 由於response設置的編碼是用來編碼的,就是講字符轉化爲字節數組,而meta設置的utf-8是用來解碼的,也就是說告訴,瀏覽器,你應該如何顯示

 

 

可是我發現,

當設置了response的編碼

設置了<meta http-equiv="Content-Type" content="text/html;charset=gbk" />

時客戶端沒有顯示亂碼,預測結果是客戶端顯示界面時應該按gbk解碼 用utf-8編碼後的字符。

關鍵在於 charset.jsp這個界面我是經過request轉發過去的,在處理中response設置的utf-8已經指定了http響應頭的編碼

 

 

req.getRequestDispatcher("/Charset.jsp").forward(req, resp);

事實上response設置的utf-8已經設置了jsp編碼的方式,同時在響應頭中指定了charset,這樣jsp設置的charset,沒有做用。

 

那麼jsp中的

<meta http-equiv="Content-Type" content="text/html;charset=gbk" />

以及在

<%@ page language="java" contentType="text/html;charset=GBK"  import="java.util.*" pageEncoding="UTF-8"%>中設置的ContentType在哪裏其做用呢?pageEncoding又是什麼呢?

如下引用

http://www.cnblogs.com/loulijun/archive/2012/03/28/2421568.html下內容

pageEncoding是jsp文件自己的編碼

  contentType的charset是指服務器發送給客戶端時的內容編碼

  JSP要通過兩次的「編碼」,第一階段會用pageEncoding,第二階段會用utf-8至utf-8,第三階段就是由Tomcat出來的網頁, 用的是contentType。

  第一階段是jsp編譯成.java,它會根據pageEncoding的設定讀取jsp,結果是由指定的編碼方案翻譯成統一的UTF-8 JAVA源碼(即.java),若是pageEncoding設定錯了,或沒有設定,出來的就是中文亂碼。

  第二階段是由JAVAC的JAVA源碼至java byteCode的編譯,不論JSP編寫時候用的是什麼編碼方案,通過這個階段的結果所有是UTF-8的encoding的java源碼。

  JAVAC用UTF-8的encoding讀取java源碼,編譯成UTF-8 encoding的二進制碼(即.class),這是JVM對常數字串在二進制碼(java encoding)內表達的規範。

  第三階段是Tomcat(或其的application container)載入和執行階段二的來的JAVA二進制碼,輸出的結果,也就是在客戶端見到的,這時隱藏在階段一和階段二的參數contentType就發揮了功效

  contentType的設定.

  pageEncoding 和contentType的預設都是 ISO8859-1. 而隨便設定了其中一個, 另外一個就跟着同樣了(TOMCAT4.1.27是如此). 但這不是絕對的, 這要看各自JSPC的處理方式. 而pageEncoding不等於contentType, 更有利亞洲區的文字 CJKV系JSP網頁的開發和展現, (例pageEncoding=GB2312 不等於 contentType=utf-8)。

  jsp文件不像.java,.java在被編譯器讀入的時候默認採用的是操做系統所設定的locale所對應的編碼,好比中國大陸就是GBK, 臺灣就是BIG5或者MS950。而通常咱們無論是在記事本仍是在ue中寫代碼,若是沒有通過特別轉碼的話,寫出來的都是本地編碼格式的內容。因此編譯器 採用的方法恰好可讓虛擬機獲得正確的資料。

  可是jsp文件不是這樣,它沒有這個默認轉碼過程,可是指定了pageEncoding就能夠實現正確轉碼了。

一、pageEncoding="UTF-8"的做用是設置JSP編譯成Servlet時使用的編碼。 
     衆所周知,JSP在服務 器上是要先被編譯成Servlet的。pageEncoding="UTF-8"的做用就是告訴JSP編譯器在將JSP文件編譯成Servlet時使用的 編碼。一般,在JSP內部定義的字符串(直接在JSP中定義,而不是從瀏覽器提交的數據)出現亂碼時,不少都是因爲該參數設置錯誤引發的。例如,你的 JSP文件是以GBK爲編碼保存的,而在JSP中卻指定pageEncoding="UTF-8",就會引發JSP內部定義的字符串爲亂碼。 
     另外,該參數還有一個功能,就是在JSP中不指定contentType參數,也不使用response.setCharacterEncoding方法時,指定對服務器響應進行從新編碼的編碼。
二、contentType="text/html;charset=UTF-8"的做用是指定對服務器響應進行從新編碼的編碼。 
    在不使用response.setCharacterEncoding方法時,用該參數指定對服務器響應進行從新編碼的編碼。

剛纔咱們就是在設置response.setCharacterEncoding(utf-8)後,設置contentType爲gbk,可是客戶端依然按照response的設置,因此response對contentType的優先級高

三、request.setCharacterEncoding("UTF-8")的做用是設置對客戶端請求進行從新編碼的編碼。

      該方法用來指定對瀏覽器發送來的數據進行從新編碼(或者稱爲解碼)時,使用的編碼。
四、response.setCharacterEncoding("UTF-8")的做用是指定對服務器響應進行從新編碼的編碼。 
     服務器在將數據發送到瀏覽器前,對數據進行從新編碼時,使用的就是該編碼。

當咱們直接經過訪問jsp的方式,轉到jsp界面時,發現 在page標籤中contentType起做用了,咱們設置的是gbk

 

 

而後咱們發現經過meta設置的編碼爲utf-8時,也並無出現亂碼,可見,瀏覽器顯示時,並無按照meta標籤中指定的編碼,而是優先使用page標籤中的contentType中設置的值。這時即便設置了字符過濾器,字符過濾器中response中的設置也不會啓用,也便是說,

當直接訪問jsp時,只會看

<%@ page language="java"  contentType="text/html;charset=gbk" import="java.util.*" pageEncoding="UTF-8"%>

的設置,(過濾器什麼的都不會起做用)

當採用servlet中請求轉發方式時,過濾器或servlet中的response的設置會起做用

當在html頁面中,使用meta標籤會起做用,當沒有meta標籤中會默認按照utf-8編碼(並不推薦默認)

下面咱們在分析一下,已開始討論的瀏覽器端的處理

默認的狀況下,若是jsp中出現這個超級連接應該如何處理呢。

<a href="http://localhost:8080/TestCharSet/test?charset=中文">點擊這裏</a>

瀏覽器解析這個本界面時按照response的設置,或者page標籤中的contentType設置(也就是響應頭中的charset)進行解析,因此這個連接中的中文天然是按照響應中的設置進行編碼,而這是一個get請求,咱們知道get請求瀏覽器須要將url進行url編碼(16進制編碼),咱們須要知道url自己的編碼是utf-8,仍是gbk,這樣服務器獲取到字符序列才能進行從新編碼,

當咱們的url是經過超級連接跳轉的,編碼方式按照本界面response的字符編碼設定。

當咱們直接經過地址欄輸入中文url,這時url會按照瀏覽器本身默認的編碼方式編碼,火狐是utf-8,360也是utf-8,至於其餘的瀏覽器是否是utf-8不肯定。

那麼post方式,瀏覽器是採用什麼編碼的呢?

和上面所說的同樣,瀏覽器會解析response設置的響應頭的charset,而後肯定post請求的主體採用什麼編碼

若是響應頭中charset是utf-8時,那麼下次它發送http請求,主體部分會是utf-8.

引用這個連接中的一段話http://www.cnblogs.com/loulijun/archive/2012/03/28/2421568.html

  response.setCharacterEncoding("UTF- 8")的做用是指定對服務器響應進行從新編碼的編碼。同時,瀏覽器也是根據這個參數來對其接收到的數據進行從新編碼(或者稱爲解碼)。因此在不管你在 JSP中設置response.setCharacterEncoding("UTF-8")或者 response.setCharacterEncoding("GBK"),瀏覽器均能正確顯示中文(前提是你發送到瀏覽器的數據編碼是正確的,好比正 確設置了pageEncoding參數等)。讀者能夠作個實驗,在JSP中設置 response.setCharacterEncoding("UTF- 8"),在IE中顯示該頁面時,在IE的菜單中選擇"查看(V)"à"編碼(D)"中能夠查看到是" Unicode(UTF-8)",而在在JSP中設置response.setCharacterEncoding("GBK"),在IE中顯示該頁面 時,在IE的菜單中選擇"查看(V)"à"編碼(D)"中能夠查看到是"簡體中文(GB2312)"。 
     瀏覽器在發送數據時,對URL和參數會 進行URL編碼,對參數中的中文,瀏覽器也是使response.setCharacterEncoding參數來進行URL編碼的。以百度和 GOOGLE爲例,若是你在百度中搜索"漢字",百度會將其編碼爲"%BA%BA%D7%D6"。而在GOOGLE中搜索"漢字",GOOGLE會將其編 碼爲"%E6%B1%89%E5%AD%97",這是由於百度的response.setCharacterEncoding參數爲GBK,而 GOOGLE的的response.setCharacterEncoding參數爲UTF-8。 
      瀏覽器在接收服務器數據和發送數據到服務器 時所使用的編碼是相同的,默認狀況下均爲JSP頁面的response.setCharacterEncoding參數(或者contentType和 pageEncoding參 數),咱們稱其爲瀏覽器編碼。固然,在IE中能夠修改瀏覽器編碼(在IE的菜單中選擇"查看(V)"à"編碼(D)"中修 改),但一般狀況下,修改該參數會使本來正確的頁面中出現亂碼。一個有趣的例子是,在IE中瀏覽GOOGLE的主頁時,將瀏覽器編碼修改成"簡體中文 (GB2312)",此時,頁面上的中文會變成亂碼,不理它,在文本框中輸入"漢字",提交,GOOGLE會將其編碼爲"%BA%BA%D7%D6",可 見,瀏覽器在對中文進行URL編碼時,使用的就是瀏覽器編碼。

 

因此至此整個瀏覽器發送http請求,服務器獲取數據,發送數據,瀏覽器接受數據中涉及的編碼基本上都已經說了,那項目中還有哪些編碼問題呢,或者哪些編碼問題隱藏比較深呢?

1.數據庫的編碼,數據庫通常不會出現編碼問題前提是咱們建立數據庫時須要指定編碼

CREATE TABLE `t_image` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `fkey` varchar(50) DEFAULT NULL,

  `src` varchar(500) NOT NULL,

  `des1` varchar(500) DEFAULT NULL,

  `des2` varchar(500) DEFAULT NULL,

  `des3` varchar(500) DEFAULT NULL,

  `des4` varchar(500) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

同時鏈接數據庫時也要制定編碼

jdbc:mysql://localhost:3306/psych?Unicode=true&characterEncoding=utf-8

這種狀況下基本上不會出現數據庫編碼問題,在這種狀況下,若是出現亂碼問題,最後考慮數據庫,由於有可能存進去的時候就是亂碼

什麼意思呢,例如你將一個 string轉化爲byte[]使用gbk編碼,而後存進數據庫,這時你若是讀取後,將byte[]按照utf-8進行解碼,自己就是錯的,後面我會說到個人一個經歷,這個經歷好久了,但bug隱藏的很深,以致於 寫博客以前,我才恍然大悟爲何出現問題。

 

2.tomcat的默認編碼問題。

爲何要說tomcat的默認編碼?剛纔不已經說了嘛,經過uriEncoding設置。此處說的默認編碼設置是tomcat自己做爲一個javaweb應用服務器,它自己就是一個進程,而一個進程實例就是一個虛擬機,咱們在java裏不多說進程,取而代之的是虛擬機,實際上,tomcat也是從main函數開始,中間有線程監聽請求,監聽到交給處理器線程處理,處理器針對http請求流進行解析,將其封裝爲request,response,其中response中封裝了outputStream,也是即將發送給客戶端的輸出流,而後映射器(tomcat5之後沒了,但道理同樣)找到咱們定義的servlet,將其加載,包裝成tomcat中的類進行業務處理。以上咱們討論的都是每層每一個階段之間交換數據出現的編碼問題。

而這裏談到的默認編碼是指tomcat進程(虛擬機)自己採用的字符編碼,能夠經過

System.out.println(java.nio.charset.Charset.defaultCharset());

查看在windows下默認是gbk,linux下默認是utf-8

通常狀況下,咱們即便在windows下面,一個java進程採用的編碼是utf-8

可是tomcat進程不一樣,它是gbk。

那這個默認字符編碼做用是什麼呢?

String string = "很開心分享經驗";

byte[] bytes = string.getBytes(UTF);                

String string2 = new String(bytes);

這段代碼執行結果是什麼呢那要看具體的環境了在windows下的tomcat,絕對是亂碼

在linux下的tomcat,就不亂,若是是默認的進程(你本身寫個main,而後這三代碼,不加任何虛擬機初始參數)那不是亂碼。

一開始我並無發現windows下的tomcat默認編碼是gbk,我在測試數據庫時,採用的是本地測試,把字符串轉化爲byte[]時,採用的是默認的,這時默認的應該是utf,而後tomcat在顯示時依然採用默認的進行解碼(gbk)這時,就出現了亂碼。然而我當時百思不得其解,誤覺得是數據庫的錯。浪費了很多時間依然沒有解決。還在懷疑是否是web請求處理中出現亂碼。這就是知識面不全,存在疏漏之處的後果。有時候咱們頗有必要創建全面深入的知識體系,不能只知其一;不知其二,即便是小知識點也要好好解決掉,不然出現了bug這些不足都會以時間成原本報復你。

 

那默認編碼如何解決呢,答案是這樣的,我找了不少初始參數可是都不對,因而想個邪招

Field field = Charset.class.getDeclaredField("defaultCharset");

field.setAccessible(true);

try {

         field.set(Charset.class,Charset.forName("GBK"));

} catch (IllegalArgumentException e) {

         //

         e.printStackTrace();

} catch (IllegalAccessException e) {

         //

         e.printStackTrace();

}

經過反射獲取靜態類的私有字段,而後修改,這時

String string = "很開心分享經驗";

byte[] bytes = string.getBytes(UTF);                

String string2 = new String(bytes);

咱們經過getBytes,以及new String().默認的都是咱們設置的gbk,或者utf-8了。

 

 

轉載自:http://blog.csdn.net/yuhaiqiang_123/article/details/51811419

相關文章
相關標籤/搜索