Java java采用的編碼、JVM平台默認字符集和外部資源的編碼


其他:http://www.cnblogs.com/lxzh/archive/2012/05/30/2526557.html

java采用的編碼是unicode編碼;

查看jvm的默認編碼:

可以使用System.out.println(Charset.defaultCharset());來查看jvm默認的字符集(我的mac為utf-8(java.nio.charset.CharsetICU[UTF-8]));

JVM的字符集編碼取的是操作系統默認的字符集編碼:

         win xp 中文版中:一般是GBK。

         win server中文版中:一般是GB18030(猜測)。

         Linux 中看本地語言環境配置文件怎么設置。

java字符串使用的是unicode編碼。 

 

其實,編碼問題存在兩個方面:JVM之內和JVM之外。
 
1、Java文件編譯后形成class
這里Java文件的編碼可能有多種多樣,但Java編譯器會自動將這些編碼按照Java文件的編碼格式正確讀取后產生class文件,這里的class文件編碼是Unicode編碼(具體說是UTF-16編碼)。
因此,在Java代碼中定義一個字符串:
String s="漢字";
不管在編譯前java文件使用何種編碼,在編譯后成class后,他們都是一樣的----Unicode編碼表示。
 
2、JVM中的編碼
JVM加載class文件讀取時候使用Unicode編碼方式正確讀取class文件,那么原來定義的String s="漢字";在內存中的表現形式是Unicode編碼。
 
當調用String.getBytes()的時候,其實已經為亂碼買下了禍根。因為此方法使用平台默認的字符集來獲取字符串對應的字節數組。在WindowsXP中文版中,使用的默認編碼是GBK,不信運行下:
public  class Test { 
         public  static  void main(String[] args) { 
                System.out.println( "當前JRE:" + System.getProperty( "java.version")); 
                System.out.println( "當前JVM的默認字符集:" + Charset.defaultCharset()); 
        } 
}
 
當前JRE:1.6.0_16 
當前JVM的默認字符集:GBK
 
當不同的系統、數據庫經過多次編碼后,如果對其中的原理不理解,就容易導致亂碼。因此,在一個系統中,有必要對字符串的編碼做一個統一,這個統一模糊點說,就是對外統一。比如方法字符串參數,IO流,在中文系統中,可以統一使用GBK、GB13080、UTF-8、UTF-16等等都可以,只是要選擇有些更大字符集,以保證任何可能用到的字符都可以正常顯示,避免亂碼的問題。(假設對所有的文件都用ASCII碼)那么就無法實現雙向轉換了。
 
要特別注意的是,UTF-8並非能容納了所有的中文字符集編碼,因此,在特殊情況下,UTF-8轉GB18030可能會出現亂碼
 
3、內存中字符串的編碼
內存中的字符串不僅僅局限於從class代碼中直接加載而來的字符串,還有一些字符串是從文本文件中讀取的,還有的是通過數據庫讀取的,還有可能是從字節數組構建的,然而他們基本上都不是Unicode編碼的,原因很簡單,存儲優化。
 
因此就需要處理各種各樣的編碼問題,在處理之前,必須明確“源”的編碼,然后用指定的編碼方式正確讀取到內存中。如果是一個方法的參數,實際上必須明確該字符串參數的編碼,因為這個參數可能是另外一個日文系統傳遞過來的。當明確了字符串編碼時候,就可以按照要求正確處理字符串,以避免亂碼。
在對字符串進行解碼編碼的時候,應該調用下面的方法:
getBytes(String charsetName)    
String( byte[] bytes, String charsetName)
 
而不要使用那些不帶字符集名稱的方法簽名,通過上面兩個方法,可以對內存中的字符進行重新編碼。
 
 
(一) JVM 默認字符集—— Charset. defaultCharset ()

Java 中,字符字節轉換時,如果不提供字符集,使用默認字符集。例如,字符串和字節數組轉換時,字節流和字符流轉換時等。

 1 String str = "中文"; 2 // 獲取JVM默認字符集 3 System.out.println("defaultCharset:" + Charset.defaultCharset()); 4 5 System.out.println("##字符串轉換成byte數組"); 6 byte[] defaultByteArray = str.getBytes(); 7 byte[] gbkByteArray = str.getBytes("GBK"); 8 byte[] utfByteArray = str.getBytes("UTF-8"); 9 System.out.println("defaultByteArray:" 10 + Arrays.toString(defaultByteArray)); 11 System.out.println("gbkByteArray:" + Arrays.toString(gbkByteArray)); 12 System.out.println("utfByteArray:" + Arrays.toString(utfByteArray)); 13 14 System.out.println("##byte數組轉換成字符串"); 15 String defaultStr = new String(defaultByteArray); 16 String gbkStr = new String(defaultByteArray, "GBK"); 17 String utfStr = new String(defaultByteArray, "UTF-8"); 18 System.out.println("defaultStr:" + defaultStr); 19 System.out.println("gbkStr:" + gbkStr); 20 // 因為utf-8是變長編碼,沒有跟[-42, -48, -50, -60]對應的用utf-8字符集的字符串,所以會亂碼 21 System.out.println("utfStr:" + utfStr); 22 23 System.out.println("##字節流轉化成字符流"); 24 // 文件中只有“中文”2個字,文件采用“GBK”編碼,共4個byte 25 BufferedReader defaultReader = new BufferedReader( 26 new InputStreamReader(new FileInputStream("src/encode.txt"))); 27 BufferedReader gbkReader = new BufferedReader(new InputStreamReader( 28 new FileInputStream("src/encode.txt"), "GBK")); 29 BufferedReader utfReader = new BufferedReader(new InputStreamReader( 30 new FileInputStream("src/encode.txt"), "UTF-8")); 31 System.out.println("defaultReader:" + defaultReader.readLine()); 32 System.out.println("gbkReader:" + gbkReader.readLine()); 33 System.out.println("utfReader:" + utfReader.readLine()); 34 35 System.out.println("##字符流轉化成字節流"); 36 BufferedWriter defaultWriter = new BufferedWriter( 37 new OutputStreamWriter(System.out)); 38 BufferedWriter gbkWriter = new BufferedWriter(new OutputStreamWriter( 39 System.out, "GBK")); 40 BufferedWriter utfWriter = new BufferedWriter(new OutputStreamWriter( 41 System.out, "UTF-8")); 42 System.out.print("defaultWriter:"); 43 defaultWriter.write(str); 44 // 這里不能用close()方法,否則System.out也被關閉,后續無輸出 45 defaultWriter.flush(); 46 System.out.print("\r\ngbkReader:"); 47 gbkWriter.write(str); 48 gbkWriter.flush(); 49 System.out.print("\r\nutfReader:"); 50 utfWriter.write(str); 51 utfWriter.flush();
Java

defaultCharset:GBK 
##字符串轉換成byte數組 
defaultByteArray:[-42, -48, -50, -60] 
gbkByteArray:[-42, -48, -50, -60] 
utfByteArray:[-28, -72, -83, -26, -106, -121] 
##byte數組轉換成字符串 
defaultStr:中文 
gbkStr:中文 
utfStr:???? 
##字節流轉化成字符流 
defaultReader:中文 
gbkReader:中文 
utfReader:???? 
##字符流轉化成字節流 
defaultWriter:中文 
gbkReader:中文 
utfReader:涓枃

 
( 二) Java 程序入口文件( main 函數所在文件) 編碼 —— System.getProperty("file.encoding")
該編碼默認值取決於 Java 程序入口文件( main 函數所在文件) 編碼 的編碼方式,具體請參考下面文章,也可以在運行 Java 程序時通過 -Dfile.encoding="GBK"  來設定,如果 -Dfile.encoding 指定的編碼方式跟 Java 程序入口文件的字符集不一致,將會導致亂碼 。也可以在程序中通過 setP roperty 方法直接設置,這種設置雖然改變了“ file.encoding ”的值,但是似乎沒什么用 。

1 System.out.println("##文件編碼是GBK,-Dfile.encoding=\"GBK\""); 2 System.out.println("file.encoding:" 3 + System.getProperty("file.encoding")); 4 // 在不存在的目錄下創建文件,查看報錯信息 5 try { 6 new File("directory/test.txt").createNewFile(); 7 } catch (IOException e) { 8 e.printStackTrace(); 9 } 10 11 // ##文件編碼是GBK,-Dfile.encoding="UTF-8" 12 System.out.println("##文件編碼是GBK,-Dfile.encoding=\"UTF-8\""); 13 System.out.println("file.encoding:" 14 + System.getProperty("file.encoding")); 15 // 在不存在的目錄下創建文件,查看報錯信息 16 try { 17 new File("directory/test.txt").createNewFile(); 18 } catch (IOException e) { 19 e.printStackTrace(); 20 }
Java

##文件編碼是GBK,-Dfile.encoding="GBK"

file.encoding:GBK

java.io.IOException: 系統找不到指定的路徑。

at java.io.WinNTFileSystem.createFileExclusively(Native Method)

at java.io.File.createNewFile(File.java:883)

at Encoding.main(Encoding.java:72)

##鏂囦歡緙栫爜鏄疓BK,-Dfile.encoding="UTF-8"

file.encoding:UTF-8

java.io.IOException: 緋葷粺鎵句笉鍒版寚瀹氱殑璺緞銆?

at java.io.WinNTFileSystem.createFileExclusively(Native Method)

at java.io.File.createNewFile(File.java:883)

at Encoding.main(Encoding.java:83)

 
(三)文件名字編碼—— System.getProperty("sun.jnu.encoding")
今天項目中遇到通過web上傳文件到文件系統時,中文文件名亂碼,一步步排查了http通訊過程中的編解碼(通過utf-8)都沒有發現問題。操作系統的默認字符集也是utf-8。 通過日志打印System.getProperty("file.encoding")也是utf-8碼。。。。一時凌亂了!!     最后通過查資料,發現通過java寫文件時文件名編碼和System.getProperty("sun.jnu.encoding")有關,打印此環境屬性發現輸出竟然是ANSI的一種編碼,終於找到問題爆發點,通過在程序中設置System.setProperty("sun.jnu.encoding","utf-8")解決問題。

通過問題解決得出結論:sun.jnu.encoding 影響文件名的創建,而 file.encoding 則影響到文件內容。

所以說,在我們使用 Java 處理中文文件的時候,如果發現文件的中文內容沒有亂碼,而文件的中文名發生亂碼,我們就應當多考慮一下 sun.jnu.encoding 和 file.encoding 的區別了。
 
 
 

(四)java對字符的處理

在java應用軟件中,會有多處涉及到字符集編碼,有些地方需要進行正確的設置,有些地方需要進行一定程度的處理。

3.1. getBytes(charset)

這是java字符串處理的一個標准函數,其作用是將字符串所表示的字符按照charset編碼,並以字節方式表示。注意字符串在java內存中總是按unicode編碼存儲的。比如"中文",正常情況下(即沒有錯誤的時候)存儲為"4e2d 6587",如果charset為"gbk",則被編碼為"d6d0 cec4",然后返回字節"d6 d0 ce c4"。如果charset為"utf8"則最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",則由於無法編碼,最后返回 "3f 3f"(兩個問號)。

3.2. new String(charset)

這是java字符串處理的另一個標准函數,和上一個函數的作用相反,將字節數組按照charset編碼進行組合識別,最后轉換為unicode存儲。參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正確的結果"4e2d 6587",但iso8859-1最后變成了"003f 003f"(兩個問號)。

因為utf8可以用來表示/編碼所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。

3.3. setCharacterEncoding()

該函數用來設置http請求或者相應的編碼。

對於request,是指提交內容的編碼,指定后可以通過getParameter()則直接獲得正確的字符串,如果不指定,則默認使用iso8859-1編碼,需要進一步處理。參見下述"表單輸入"。值得注意的是在執行setCharacterEncoding()之前,不能執行任何getParameter()。java doc上說明:This method must be called prior to reading request parameters or reading input using getReader()。而且,該指定只對POST方法有效,對GET方法無效。分析原因,應該是在執行第一個getParameter()的時候,java將會按照編碼分析所有的提交內容,而后續的getParameter()不再進行分析,所以setCharacterEncoding()無效。而對於GET方法提交表單是,提交的內容在URL中,一開始就已經按照編碼分析所有的提交內容,setCharacterEncoding()自然就無效。

對於response,則是指定輸出內容的編碼,同時,該設置會傳遞給瀏覽器,告訴瀏覽器輸出內容所采用的編碼。

3.4. 處理過程

下面分析兩個有代表性的例子,說明java對編碼有關問題的處理方法。

3.4.1. 表單輸入

User input  *(gbk:d6d0 cec4)  browser  *(gbk:d6d0 cec4)  web server  iso8859-1(00d6 00d 000ce 00c4)  class,需要在class中進行處理:getbytes("iso8859-1")為d6 d0 ce c4,new String("gbk")為d6d0 cec4,內存中以unicode編碼則為4e2d 6587。

l 用戶輸入的編碼方式和頁面指定的編碼有關,也和用戶的操作系統有關,所以是不確定的,上例以gbk為例。

l 從browser到web server,可以在表單中指定提交內容時使用的字符集,否則會使用頁面指定的編碼。而如果在url中直接用?的方式輸入參數,則其編碼往往是操作系統本身的編碼,因為這時和頁面無關。上述仍舊以gbk編碼為例。

l Web server接收到的是字節流,默認時(getParameter)會以iso8859-1編碼處理之,結果是不正確的,所以需要進行處理。但如果預先設置了編碼(通過request. setCharacterEncoding ()),則能夠直接獲取到正確的結果。

l 在頁面中指定編碼是個好習慣,否則可能失去控制,無法指定正確的編碼。

3.4.2. 文件編譯

假設文件是gbk編碼保存的,而編譯有兩種編碼選擇:gbk或者iso8859-1,前者是中文windows的默認編碼,后者是linux的默認編碼,當然也可以在編譯時指定編碼。

Jsp  *(gbk:d6d0 cec4)  java file  *(gbk:d6d0 cec4)  compiler read  uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)  compiler write  utf(gbk: e4b8ad e69687; iso8859-1: *)  compiled file  unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)  class。所以用gbk編碼保存,而用iso8859-1編譯的結果是不正確的。

class  unicode(4e2d 6587)  system.out / jsp.out  gbk(d6d0 cec4)  os console / browser。

l 文件可以以多種編碼方式保存,中文windows下,默認為ansi/gbk。

l 編譯器讀取文件時,需要得到文件的編碼,如果未指定,則使用系統默認編碼。一般class文件,是以系統默認編碼保存的,所以編譯不會出問題,但對於jsp文件,如果在中文windows下編輯保存,而部署在英文linux下運行/編譯,則會出現問題。所以需要在jsp文件中用pageEncoding指定編碼。

l Java編譯的時候會轉換成統一的unicode編碼處理,最后保存的時候再轉換為utf編碼。

l 當系統輸出字符的時候,會按指定編碼輸出,對於中文windows下,System.out將使用gbk編碼,而對於response(瀏覽器),則使用jsp文件頭指定的contentType,或者可以直接為response指定編碼。同時,會告訴browser網頁的編碼。如果未指定,則會使用iso8859-1編碼。對於中文,應該為browser指定輸出字符串的編碼。

l browser顯示網頁的時候,首先使用response中指定的編碼(jsp文件頭指定的contentType最終也反映在response上),如果未指定,則會使用網頁中meta項指定中的contentType。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM