本周遇到一個java亂碼問題,於是對java的編碼問題做了一些實驗和瞭解。簡單分析如下:
先看下如下代碼:
import java.io.UnsupportedEncodingException;
public class CharSetTest {
public static void main(String[] args) throws UnsupportedEncodingException {
String test = " 籃球 " ;
byte [] defaultResult = test.getBytes();
for ( byte e : defaultResult) {
System. out .print(e + " " );
}
System. out .println(System. getProperty ( "file.encoding" ));
System. out .println( "test=" + test);
}
}
1.執行 javac CharSetTest.java,能正常編譯,但是得到如下警告:
CharSetTest.java:5: warning: unmappable character for encoding ASCII
String test = "????";
分析一下為什麼會這樣呢。對於java編譯器來說,CharSetTest.java就是一個文字檔,java編譯器要解析 這個文字檔並編譯產生.class檔案。分析了下原因大概是這樣的:CharSetTest.java一定是以某一種編碼格式來儲存的,所以java編 譯器一定要知道該文字檔時用什麼來編碼的,如果沒有指定就用預設認為檔案的編碼格式是” ANSI_X3.4-1968”(不同環境可能不一樣),所以就會發現無法解釋的中文而出現了亂碼。
那麼以上問題該如何解決,就是要在編譯的時候告訴編譯器,需要編譯的java檔案的編碼格式,否則編譯器有可能遇到不能理解的字元就當做亂碼處理了。由於 CharSetTest.java是GBK格式的,所以通過如下命令完成:
Javac CharSetTest.java –encoding=GBK。
2. 通過執行Javac CharSetTest.java –encoding=GBK,已經能得到正確的class檔案了,但是執行 java CharSetTest,結果如下:
63 63 ANSI_X3.4-1968
test=??
那麼既然已經正確編譯了,為什麼得到的輸出結果還會是亂碼呢。前面已經可以肯定.class檔案裡面存放的中文字串是正確的了,那原因肯定是在JVM 從.class檔案讀取這個字串位元組流並構建String對象的時候採用了錯誤的字元編碼來構建位元組流。進而導致從JVM輸出字串的位元組流到我們控制 台的時候,出現亂碼。那麼很顯然,我們必須告訴jvm我們控制台的編碼,或者我們希望它採用什麼字元編碼來構建位元組流。如果沒有告訴jvm,那麼檔案的編 碼格式是” ANSI_X3.4-1968”(不同環境可能不一樣)。
假設我們控制台是GBK的編碼,那麼只要我們正確告訴它,它就能正確的返回位元組流了。那麼原因就比較簡單,我們沒有正確告訴JVM我們需要它構造字串 輸出資料流的時候應該採用的編碼格式。那麼該如何處理呢。通過如下命令:
java -Dfile.encoding=GBK CharSetTest
得到結果:
-64 -70 -57 -14 GBK
test=籃球
以上實驗在eclipse下面並不會成立,因為eclipse會幫我們做一些判斷。同時不同的環境可能也不 一樣。
最後總結:
Java的class檔案採用UTF-8,在JVM裡面採用UTF-16。整個過程中編碼轉換大概可以看下圖:
從上圖可以理解不管採用那種格式的源檔案,只要正確告訴編譯器,編譯器就會得到正確的結果。同時只要告訴JVM正確的輸出資料流需要的編碼格式,JVM總會返 回正確編碼格式的輸出資料流。
那麼要想不產生亂碼要注意兩個環節:
1. 告訴編譯器你的源檔案編碼。
2. 告訴jvm你顯示或者構造字串輸出資料流時希望的編碼。
尤其JSP亂碼時要注意request請求採用的編碼和解析request時候採用的編碼是否一致,response的編碼和html charset的編碼是否一致。
同時我們常遇到jsp、資料庫等亂碼問題可以找一下是否是以下兩種原因:
1. 誤解型:a檔案是GBK編碼,但是你以為是UTF-8型編碼,所以用UTF-8來理解它,就會出現亂碼。
2. 無能為力型:a檔案時GBK編碼,你也知道它是GBK編碼,但是你想轉換成ISO-8859-1的方式來顯示,但是GBK裡面有很多字元時ISO- 8859-1所不能解釋的,這時也會出現亂碼。