電腦只能處理011001這樣的位元字,字元是日常生活中我們使用的符號,為了電腦能夠儲存、傳輸和展示字元,所以,我們需要把字元轉換為0110000這樣的二進位碼。這就是所謂編碼。相反,把011000這樣的二進位碼轉換為字元的過程就是解碼!JAVA裡,char表示一個字元,String表示字串!
具體把哪個字元對應表到哪個二進位串上,是由國家(國家標準)、國際組織(國際標準)等決定的!
一般不用二進位串來表示某個字元的編碼(因為寫起來、閱讀起來都很麻煩),所以一般是用十六進位的串來表示某個字元的編碼。
因此:
字元 <--> 十六進位串,之間的映射的集合(因為有很多字元),就構成了字元集。
你經常見到的字元集是:Unicode、UTF-8、GB2312、GB18030、GBK、Big-5、ISO-8859-1(又名:Latin-1)
Unicode、UTF-8能夠支援目前世界上所有語言文字的字元 GB2312是舊的國家標準,不支援繁體漢字 GBK不是國家標準,但支援繁體漢字 GB18030是新的國家標準,也支援繁體漢字 Big-5,能支援繁體漢字
ISO-8859-1,不支援漢字,英美等拉丁語系的國家常用這個編碼
舉例:
public class EncodingTest01 { public static void main(String[] args) throws Exception{ String s = "中國";
byte[] bs = s.getBytes("GB18030");
System.out.println(Utils.byteArrayToHex(bs));
}
}
public class Utils { private static char[] hexDigits = { '0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'
};
public static String byteArrayToHex(byte[] bs){ StringBuffer sb = new StringBuffer();
for(byte b:bs){
sb.append(hexDigits[(b >> 4) & 0x0000000F]); sb.append(hexDigits[b & 0x0000000F]); sb.append(",");
}
return sb.toString();
}
}
“中國”,這兩個簡體漢字,在不同的字元集中,用上述程式輸出,其編碼分別為: Unicode – FE,FF,4E,2D,56,FD, UTF-8 - E4,B8,AD,E5,9B,BD
GB18030 - D6,D0,B9,FA, ISO-8859-1 - 3F,3F, 解釋: 關於Unicode
用Unicode編碼之後的“中國”,其編碼的前面有FE,FF兩個額外的位元組。這兩個位元組稱為
BOM(Byte Order Mark)。“中”這個漢字,它的Unicode編碼是:4E,2D,那麼在傳輸的過程中,是把4E放在前面,還是把2D放在前面呢?這有BOM來決定。如果BOM是FEFF(稱為Big Endian),表示4E在前;如果BOM是FFFE(稱為Little Endian),表示2D在前。
也就是說,如果你的檔案編碼是Unicode(也可以叫做UTF-16),那麼檔案開頭的位元組就是:FEFF(以這種方式開頭的編碼叫UTF-16BE)或FFFE(以這種方式開頭的編碼叫做UTF-16LE)
UTF-16BE、UTF-16、Unicode是一樣的!
關於UTF-8
UTF-8編碼的檔案,其檔案頭也有一段標識:EF BB BF
其它編碼的檔案,其檔案頭沒有什麼標識!!!
對於使用ISO-8859-1編碼方式來對“中國”二字進行編碼之後,得到的是:3F 3F,這是因為ISO-8859-1字元集中根本就沒有“中國”這兩個字元!所以,它用3F來代替!
解碼
解碼,實際上就是把二進位流(也就是位元組流,即位元組數組)轉換為字元。位元組數組是什麼樣的編碼(即用哪個字元集對它編碼),你必需使用相同的字元集來解碼!
public class EncodingTest02 { public static void main(String[] args) throws Exception{
byte[] bs = {(byte)0xD6,(byte)0xD0,(byte)0xB9,(byte)0xFA};
String s = new String(bs,"GB18030"); System.out.println(s);
}
}
像上述程式所示的那樣,我們知道:D6D0 B9FA 是“中國”兩個字元的“GB18030”編碼,所以,你可以使用這個字元集來把其對應的位元組數組解碼為字元!
Java中的字元
你首先要理解的是,JAVA可以把字元儲存在記憶體中(即也是以0110010這樣的方式儲存在記憶體中)!那麼,JAVA把字元儲存在記憶體中的時候,它使用的是什麼編碼呢?答案是:Unicode
因此,我們知道,“中國”,這兩個漢字,在記憶體中,是以“4E,2D,56,FD”這種方式存在的!
不過要注意,JAVA的class檔案,是以UTF-8方式編碼的!JAVA虛擬機器讀取class檔案的時候,通過UTF-8編碼把class檔案讀入記憶體,並轉換為UTF-16(即Unicode)。
因此,new String(byte[],“GB18030“)的真正意思是:把位元組流(它是以GB18030方式編碼的)轉換為Unicode的位元組流存在記憶體中!
4E,2D,56,FD
中國
E4,B8,AD,E5,9B,BD
D6,D0,B9,FA
3F,3F
getBytes(“ISO-8859-1”)
getBytes(“GB18030”)
getBytes(“UTF-8”)
如所示,假設有“中國”兩個漢字,通過getBytes(“encoding”)能將其轉換為不同的編碼!
再看,假設有一個位元組流,現在想把它轉換為字串:
E4,B8,AD,E5,9B,BD4E,2D,56,FD
中國
new String(byte[],”UTF-8”)
E4,B8,AD,E5,9B,BD
new String(byte[],”ISO-8859-1”)
getBytes(“ISO-8859-1”)
//"中國”兩個字元的UTF-8編碼E4,B8,AD,E5,9B,BD
byte[] bs = {(byte)0xE4,(byte)0xB8,(byte)0xAD,(byte)0xE5,(byte)0x9B,(byte)0xBD};
String s = new String(bs,"ISO-8859-1"); //長度為6System.out.println(s); //得到亂碼
byte[] newbs = s.getBytes("ISO-8859-1");
String newString = new String(newbs,"UTF-8");System.out.println(newString); //正確
一個本來是UTF-8編碼的位元組流,如果你把它當成ISO-8859-1進行轉換,ISO-8859-1的規則是每個位元組一個字元,所以得到了長度為6的字串,如果你將它輸出,將是亂碼!現在你可以反向轉換,通過ISO-8859-1編碼得到它的位元組流,這將原封不動得到一個位元組流,然後,你又可以對這個位元組流進行正確的解碼了!
簡單總結一下
如果你拿到一個位元組流,你需要清楚的知道,你這個位元組流是用哪個字元集進行編碼的! 開源工具:chardet,可以用來猜測某一段位元組流究竟是什麼編碼!
如果你拿到一個字串,你發現它輸出的時候是亂碼,那麼,你究竟能不能通過某種技術手段解決這個亂碼問題呢?答案是:不一定! 因為你拿到了一個字串,所以,可以肯定,你拿到的字串是由於某人將它按照某種
字元集將位元組流進行瞭解碼而得到的字串!
a) 假如某人是按照ISO-8859-1字元集對位元組流進行解碼而得到的字串,那麼,恭
喜你,你將可以通過某種技術手段,把你手中的亂碼字串轉換為正確的字串!這種技術手段就是:先getBytes(“ISO-8859-1”)得到一個位元組流,然後把這個位元組流用new String(byte[],“正確的字元集”)轉換為正確的字串即可!
b) 假如某人不是按照ISO-8859-1字元集對位元組流進行解碼的,那麼,很不幸,有很
大的可能是你沒有辦法將它再正確地還原了!
頁面編碼
通過GET方法向後台遞交的請求
向伺服器遞交的請求中,字元也是需要進行編碼之後傳輸的!瀏覽器將會自動對URL地址中的漢字進行編碼之後傳輸。如果你直接在瀏覽器地址欄輸入URL地址及漢字,那麼瀏覽
器將自動使用當前作業系統的預設字元集進行編碼(中文作業系統就是GB2312)之後傳輸到伺服器;如果你在某個頁面上點擊一個連結(此連結後面附帶著中文漢字),那麼,瀏覽器將自動根據這個頁面所使用的字元集來對漢字進行編碼之後,傳輸到伺服器!
比如“中國”兩個漢字,假如頁面編碼是GB18030(或GBK或GB2312),那麼向後傳輸的串就是:%D6%D0%B9%FA
比如:http://localhost:8080/myservlet/AddUser?username=%D6%D0%B9%FA
我們後台使用request.getParameter(“username”)來得到一個字串,結果發現是亂碼,那麼可以肯定,某人幫我們將從用戶端傳輸過來的位元組流按照某種字元集解碼成了字串!某人就是應用伺服器(TOMCAT),某種字元集就是應用伺服器的預設字元集,是ISO-8859-1。所以,很幸運,還有救!
String username = request.getParameter("username");
//將它轉換為正確的字串!
byte[] bs = username.getBytes("ISO-8859-1"); username = new String(bs,"GB18030"); //正確
System.out.println(username);
當然,為了避免每次都這樣幹,我們乾脆修改TOMCAT對於URL編碼的預設解碼字元集即可!
修改server.xml檔案中的下面的語句,加上URIEncoding=“GB18030“的配置即可: <Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="GB18030"/>
通過POST向後台遞交的請求
瀏覽器將按照表單所在的頁面所使用的字元集對資料進行編碼之後傳輸!
在TOMCAT中配置的URIEncoding只對GET方式遞交的請求中的資料有效,而對於POST請求,你必須在調用request.getParameter(“xxx”)之前,調用:request.setCharacterEncoding("GB18030"); !
Response的編碼
在調用response.getWriter()之前,調用response.setCharacterEncoding("GB18030");
response 預設採用iso_8859-1 編碼、解碼。。中文會亂碼。現在改用GB18030就不會亂碼了。
http://wenku.baidu.com/view/14a91366caaedd3383c4d331.html