字符是各種文字和符號的總稱,包括各個國家文字、標點符號、圖形符號、數字等。字符集是多個字符的集合,字符集種類較多,每個字符集包含的字符個數不同,常見字符集有:ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。計算機要準確的處理各種字符集文字,需要進行字符編碼,以便計算機能夠識別和存儲各種文字。如Unicode字符集可依不同需要以UTF-8、UTF-16、UTF-32等方式編碼。
編碼和字符集不同。字符集只是字符的集合,不一定適合作網絡傳送、處理,有時須經編碼後才能應用。對於一個字符集來說要正確編碼轉碼一個字符需要三個關鍵元素:字庫表、編碼字符集、字符編碼。字庫表是一個相當於所有可讀或者可顯示字符的數據庫,字庫表決定了整個字符集能夠展現表示的所有字符的範圍。編碼字符集,即用一個編碼值來表示一個字符在字庫中的位置。字符編碼,將編碼字符集和實際存儲數值之間的轉換關係。
正確使用字符串的getBytes方法
java提供了將String轉化爲byte[]的幾種方法,具體說明如下:
不帶參數的方法使用平臺的默認字符集,另外兩個方法都是按照指定字符集獲取字節數組,window下默認的字符集是GBK,驗證方式如下:
進入cmd --> 點擊標題欄右鍵 --> 屬性 --> 選項 --> 當前代碼頁
Linux下,輸入:echo $LANG進行驗證(默認是UTF-8)
當調用無參getBytes()方法時,實際上先獲取默認字符集Charset.defaultCharset(),然後根據該字符集進行獲取字節數組;爲了實現更加靈活的控制,java引入了file.encoding屬性,JVM啓動之前如果未指定file.encoding這個屬性,這個屬性就會默認爲操作系統編碼方式, JVM啓動如果指定了file.encoding這個屬性,整個項目都會用這個屬性(file.encoding在運行時設置無效)。
下面使用一個簡單的測試例子來說明
通過上面的瞭解,無參的getBytes方法會存在很多不確定性,會與預期的不一致,所以不要使用缺省的方法,應當根據約定的編碼方式進行轉換處理。
java的URL編碼和解碼
javascript的URL編碼和解碼
encodeURI()
ecodeURI()
Javascript中真正用來對URL編碼的函數。對整個URL進行編碼,因此除了常見的符號以外,對其他一些在網址中有特殊含義的符號「; / ? : @ & = + $ , #」,也不進行編碼。編碼後,它輸出符號的UTF-8形式,並且在每個字節前加上%
encodeURIComponent()
decodeURIComponent()
它用於對URL的組成部分進行個別編碼,而不用於對整個URL進行編碼。在encodeURI()中不被編碼的符號,在encodeURIComponent()中統統會被編碼。至於具體的編碼方法,兩者是一樣
在web應用中,經常會出現亂碼問題,本質上是由於編碼不一致導致,下面對Form表單的POST提交方式進行分析
爲了避免出現亂碼,需要保存編碼一致
String nameB = request.getParameter("name");
String nameA = new String(nameB.getBytes("編碼B"),"編碼A");//還原爲發送請求真實的編碼
1、文檔編碼(網頁右鍵 --> 編碼)
決定文檔編碼主要來自文件的描述
JSP:<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
HTML:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
JSP示例(GBK編碼):
name對應的值「立思辰」進行了雙字節轉碼(GBK)%C1%A2%CB%BC%B3%BD
HTML示例(UTF-8編碼):
name對應的值「立思辰」進行了三字節轉碼(UTF-8)%E7%AB%8B%E6%80%9D%E8%BE%B0
2、服務器解碼
tomcat中,如果未指定character encoding ,則默認使用ISO-8859-1
上述JSP 表單提交時,通過從ISO-8859-1到GBK轉換即可
String nameISO = request.getParameter("name");
String nameGBK = new String(nameISO.getBytes("ISO-8859-1"),"GBK");
上述HTML 表單提交時,通過從ISO-8859-1到UTF-8轉換即可
String nameISO = request.getParameter("name");
String nameUTF8 = new String(nameISO.getBytes("ISO-8859-1"),"UTF-8");
在實際應用中,爲了避免每次獲取參數都進行轉碼,有以下幾種方式
A、在每個servlet/JSP中設置request.setCharacterEncoding(String charEncoding)
JSP示例(GBK編碼):
request.setCharacterEncoding("GBK");
String nameGBK = request.getParameter("name");
HTML示例(UTF-8編碼):
request.setCharacterEncoding("UTF-8");
String nameUTF8 = request.getParameter("name");
B、設置統一的過濾器設置request.setCharacterEncoding(String charEncoding)
通過過濾器統一設置字符編碼(不需要在每個文件中進行設置)
Servlet中只要直接獲取參數值String nameGBK = request.getParameter("name");
這種方式,也是目前比較流行的處理方式,項目使用約定好的字符編碼,前後保持一致,對於特殊的servlet/jsp 可以在開始位置進行重置request.setCharacterEncoding
form表單的提交get方式
地址欄效果,等價與在瀏覽器地址欄輸入地址訪問,對應的值按照GBK進行編碼
第一個語句打印亂碼,第二個能正常顯示
ajax提交
相應的servlet自動按照UTF-8進行解析
當第一次執行request.getParameter() 進行參數解析,優先解析Url參數,再解析body部分
決定body解析編碼的依次是:
1、是否設置request.setCharacterEncoding()
2、是否設置header 中Content-type 對應的charset
3、以上都沒設置,默認ISO-8859-1
決定url解析編碼:
1、server.xml 設置URIEncoding
這樣對於上面模擬的form get提交方式就能正常獲取參數值
2、server.xml 設置useBodyEncodingForURI="true"
這種方式需要在調用前進行設置request.setCharacterEncoding()
3、server.xml 設置URIEncoding和useBodyEncodingForURI
設置useBodyEncodingForURI爲true時,如果未進行setCharacterEncoding則使用URIEncoding對應的編碼進行解析,若設置了CharacterEncoding,這按照設定的字符編碼值進行解析
4、上述都未設置,則使用默認編碼(ISO-8859-1)進行解析
在前後交互時需要避免UTF8和GBK之間的相互轉碼
前臺:
後臺:
輸出:
引起這種現象的本質原因在於兩個字符集特點所引發的,一個漢字UTF-8是3個字節,GBK則是2個字節。