Java Web中的中文編碼

1、爲什麼需要編碼

        不知道大家有沒有想過一個問題,那就是爲什麼要編碼?我們能不能不編碼?要回答這個問題,必須要回答計算機是如何表示我們人類能夠理解的符號的,這些符號也就是我們人類使用的語言。由於人類的語言太多了,表示這些語言的符號太多了,無法用計算機中的一個基本存儲單元——字節(byte)來表示,因而必須要經過拆分或一些翻譯工作,才能讓計算機理解我們的語言。

        我們可以把計算機能夠理解的語言假定爲英語,其他語法要能夠在計算機中使用,必須得經過一次翻譯,把它翻譯成英語。這個翻譯得過程就是編碼。所以可以想象,只要不是說英語得國家,要使用計算機就必須經過編碼。這看起來有些霸道,但這就是現狀。所以總結起來,編碼的原因有以下幾條:

  • 在計算機中存儲信息的最小單元是1個字節,即8個bit,所以能表示的字符範圍是0~255個。
  • 人類要表示的符號太多,無法用1個字節來完全表示。

要解決這個矛盾必須要有一個新的數據結構char,而從char導byte必須編碼。

2、如何翻譯

        計算機中提供了很多種翻譯方式,常見的有ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16等,它們都可以被看作字典,他們規定了轉化的規則,按照這個規則就可以讓計算機正確地表示我們的字符。目前的編碼格式很多,如GB2312、GBK、UTF-8、UTF-16都可以表示漢字,那我們到底選擇那種編碼格式來存儲漢字呢?這就要考慮其它因素了。例如,是存儲空間重要還是編碼效率重要,下面簡單的介紹下這幾種編碼格式。

1、ASCII碼

         學過計算機的人都知道ASCII碼,總共有128個,用1個字節的低7位表示,0~31是控制字符如換行、回車、刪除等,32~126是打印字符,可以通過鍵盤輸入並且能夠顯示出來。

2、GBK

        GBK全稱是《漢字內碼擴展範圍》,是國家技術監督局爲Window 95所制定的新的漢字內碼範圍,它的出現是爲擴展GB2312,並加入更多的漢字。它的編碼範圍是8140~FEFE(去掉XX7F),總共有23940個碼位,它能表示21003個漢字,它的編碼是和GB2312兼容的,也就是說用GB2312編碼的漢字可以用GBK來解碼,並且不會有亂碼。

3、UTF-16

        說到UTF必須提到Unicode(Universal Code 統一碼),ISO試圖創建一個全新的超語言字典,世界上所有的語言都可以通過這個字典來相互翻譯。可想而知這個字典是多麼複雜。

        UTF-16具體定義了Unicode字符在計算機中的存取方法。UTF-16用兩個字節來表示Unicode的轉化格式,它採用定長的表示方法,即不論什麼字符都可以用兩個字節表示。兩個字節是16個bit,所以叫UTF-16。UTF-16表示字符非常方便,每兩個字節表示一個字符,這就大大簡化了字符串的操作,這也是Java以UTF-16作爲內存的字符串存儲格式的一個很重要的原因。

4、UTF-8

         UTF-16統一採用兩個字節來表示一個字符,雖然在表示上非常方便、簡單,但是也有其缺點,有很大一部分字符用一個字節就可以表示的現在要用兩個字節表示,存儲空間放大了一倍,在現在的網絡帶寬還非常有限的情況下,這樣會增大網絡的傳輸的流量,而且也沒必要。而UTF-8採用了一種變長技術,每個編碼區域都有不同的字碼長度。不同類型的字符可以由1~6個字節組成。

UTF-8有以下編碼規則:

  • 如果是1個字節,最高位(第8位)爲0,則表示這個1是ASCII字符(00~7F)。可見,所有ASCII編碼已經是UTF-8了。
  • 如果是1個字節,以11開頭,則連續的1的個數暗示這個字符的字節數,例如:110xxxxx代表它是雙字節UTF-8字符的首字節。
  • 如果是1個字節,以10開始,表示它不是首字節,則需要向前查找才能得到當前字符的首字節。

3、Java Web中編碼

          把整型數字1234567當作字符來存儲,則採用UTF-8編碼將會佔用7個字節,採用UTF-16編碼將會佔用14個字節,但是把它當成int類型的數字來存儲時則只需要4個字節。所以看一段文本的大小,只看字符本身的長度時沒有意義的,即使時一樣的字符,採用不同的編碼最終存儲的大小也會不同,所以從字符到字節一定要看編碼類型。 

        當我們在計算機中的某個文本編輯器裏輸入某個漢字時,它到底時怎樣表示的。我們知道,在計算機裏所有的信息都是以0和1表示的,那麼一個漢字,它到底是多少個0和1呢。我們能夠看到的漢字都是以字符形式出現的,例如,在java中“淘寶”兩個字符在計算機中的十進制數值是28120和23453,16進制數值是6bd8和5d9d,即這兩個字符是由這兩個數字唯一表示的。在java中一個char是16bit,相當於兩個字節,所以兩個漢字用char表示,在內存中會佔用相當於4個字節的空間。

          把這兩個問題搞清楚,我們來看下java web中哪些地方可能存在編碼轉換,列出如下4個:

1、URL中編碼

URL的幾個組成部分如下:

       get請求,URL的pathinfo(路徑)和Query String(參數)的編碼字符集不同,瀏覽器將URL中非ASCⅡ碼字符按某種字符集轉換爲16進制到字符加上%。

  • 對URL的URI部分進行解碼的字符集在<Connector URIEncoding="UTF-8"/>中定義,若沒有定義則以默認編碼ISO-8859-1解析。有中文URL時最好把URIEncoding設置爲UTF-8編碼。
  • QueryString的解碼字符集要麼是Header中ContentType定義的Charset,要麼默認是ISO-8859-1,要使用ContentType中定義的編碼,就要設置useBodyEncodingForURI:<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>。這個配置項不是對整個URI都採用BodyEncoding編碼,僅僅是對QueryString使用BodyEncoding解碼。

2、HTTP Header編碼

        客戶端發起的HTTP請求除了URL外,還可能會在Header中傳遞其他參數(如Cookie)。對Header中的項進行解碼默認使用ISO-8859-1,且不能設置Header其他的解碼格式,若設置的Header中有非ASCII字符,解碼中肯定會出現亂碼。若一定要傳遞,則調用Tomcat中的URLEncoder編碼,再添加到Header中,這樣在從瀏覽器到服務器的傳遞過程中就不會丟失信息了。

3、POST表單的編解碼

        POST表單的參數傳遞方式是通過HTTP的BODY傳遞到服務器的,當提交時先根據ContentType中的字符集進行解碼,字符集編碼可以由request.setCharacterEncoding(charset)來設置。

         此外務必注意:對POST表單提交參數的解碼是發生在getParameter時,所以在第一次調用request.getParameter方法之前就要先設置request.setCharacterEncoding(charset)方法。

        關於上傳的文件編碼:也是使用ContentType定義的字符集編碼,不過上傳文件是以字節流的方式傳輸到服務器的本地臨時目錄,此過程尚不涉及字符編碼,只有當文件內容添加到parameters時才進行編碼。

4、HTTP BODY的編解碼

         編解碼字符集通過response.setCharacterEncoding來設置,通過Header的Content-Type返回客戶端。若Header中沒有Content-Type,瀏覽器會根據<meta http-equiv="Content-Type" content="text/html; charset=utf-8">中的charset來解碼,若依然沒有該屬性,瀏覽器則使用默認編碼。

         補充:使用JDBC來存取數據時要和數據的內置編碼保持一致,可以設置JDBC URL來指定:jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章