電腦只能處理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的字節流存在內存中!
如上圖所示,假設有“中國”兩個漢字,通過getBytes(“encoding”)能將其轉換為不同的編碼!
再看下圖,假設有一個字節流,現在想把它轉換為字符串:
一個本來是UTF-8編碼的字節流,如果你把它當成ISO-8859-1進行轉換,ISO-8859-1的規則是每個字節一個字符,所以得到了長度為6的字符串,如果你將它輸出,將是亂碼!現在你可以反向轉換,通過ISO-8859-1編碼得到它的字節流,這將原封不動得到一個字節流,然后,你又可以對這個字節流進行正確的解碼了!
簡單總結一下
如果你拿到一個字節流,你需要清楚的知道,你這個字節流是用哪個字符集進行編碼的!
如果你拿到一個字符串,你發現它輸出的時候是亂碼,那么,你究竟能不能通過某種技術手段解決這個亂碼問題呢?答案是:不一定!
l 因為你拿到了一個字符串,所以,可以肯定,你拿到的字符串是由于某人將它按照某種字符集將字節流進行了解碼而得到的字符串!
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");
即可!
或者:
在設置ContentType的時候設置也可以:
response.setContentType("text/html;charset=GB18030");
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/li_tengfei/archive/2010/12/25/6098099.aspx