理清Java中的編碼解碼轉換


1、字符集及編碼方式

概括:字符編碼方式及大端小端

詳細:徹底理解字符編碼

 可以通過Charset.availableCharsets()獲取Java支持的字符集,以JDK8為例,得到其支持的字符集:

  1         SortedMap<String, Charset> charsets = Charset.availableCharsets();
  2         System.out.println(charsets.size());
  3         for (String key : charsets.keySet()) {
  4             System.out.println(key + ":  " + charsets.get(key));
  5         }
  6 
  7 
  8 //結果
  9 169
 10 Big5:  Big5
 11 Big5-HKSCS:  Big5-HKSCS
 12 CESU-8:  CESU-8
 13 EUC-JP:  EUC-JP
 14 EUC-KR:  EUC-KR
 15 GB18030:  GB18030
 16 GB2312:  GB2312
 17 GBK:  GBK
 18 IBM-Thai:  IBM-Thai
 19 IBM00858:  IBM00858
 20 IBM01140:  IBM01140
 21 IBM01141:  IBM01141
 22 IBM01142:  IBM01142
 23 IBM01143:  IBM01143
 24 IBM01144:  IBM01144
 25 IBM01145:  IBM01145
 26 IBM01146:  IBM01146
 27 IBM01147:  IBM01147
 28 IBM01148:  IBM01148
 29 IBM01149:  IBM01149
 30 IBM037:  IBM037
 31 IBM1026:  IBM1026
 32 IBM1047:  IBM1047
 33 IBM273:  IBM273
 34 IBM277:  IBM277
 35 IBM278:  IBM278
 36 IBM280:  IBM280
 37 IBM284:  IBM284
 38 IBM285:  IBM285
 39 IBM290:  IBM290
 40 IBM297:  IBM297
 41 IBM420:  IBM420
 42 IBM424:  IBM424
 43 IBM437:  IBM437
 44 IBM500:  IBM500
 45 IBM775:  IBM775
 46 IBM850:  IBM850
 47 IBM852:  IBM852
 48 IBM855:  IBM855
 49 IBM857:  IBM857
 50 IBM860:  IBM860
 51 IBM861:  IBM861
 52 IBM862:  IBM862
 53 IBM863:  IBM863
 54 IBM864:  IBM864
 55 IBM865:  IBM865
 56 IBM866:  IBM866
 57 IBM868:  IBM868
 58 IBM869:  IBM869
 59 IBM870:  IBM870
 60 IBM871:  IBM871
 61 IBM918:  IBM918
 62 ISO-2022-CN:  ISO-2022-CN
 63 ISO-2022-JP:  ISO-2022-JP
 64 ISO-2022-JP-2:  ISO-2022-JP-2
 65 ISO-2022-KR:  ISO-2022-KR
 66 ISO-8859-1:  ISO-8859-1
 67 ISO-8859-13:  ISO-8859-13
 68 ISO-8859-15:  ISO-8859-15
 69 ISO-8859-2:  ISO-8859-2
 70 ISO-8859-3:  ISO-8859-3
 71 ISO-8859-4:  ISO-8859-4
 72 ISO-8859-5:  ISO-8859-5
 73 ISO-8859-6:  ISO-8859-6
 74 ISO-8859-7:  ISO-8859-7
 75 ISO-8859-8:  ISO-8859-8
 76 ISO-8859-9:  ISO-8859-9
 77 JIS_X0201:  JIS_X0201
 78 JIS_X0212-1990:  JIS_X0212-1990
 79 KOI8-R:  KOI8-R
 80 KOI8-U:  KOI8-U
 81 Shift_JIS:  Shift_JIS
 82 TIS-620:  TIS-620
 83 US-ASCII:  US-ASCII
 84 UTF-16:  UTF-16
 85 UTF-16BE:  UTF-16BE
 86 UTF-16LE:  UTF-16LE
 87 UTF-32:  UTF-32
 88 UTF-32BE:  UTF-32BE
 89 UTF-32LE:  UTF-32LE
 90 UTF-8:  UTF-8
 91 windows-1250:  windows-1250
 92 windows-1251:  windows-1251
 93 windows-1252:  windows-1252
 94 windows-1253:  windows-1253
 95 windows-1254:  windows-1254
 96 windows-1255:  windows-1255
 97 windows-1256:  windows-1256
 98 windows-1257:  windows-1257
 99 windows-1258:  windows-1258
100 windows-31j:  windows-31j
101 x-Big5-HKSCS-2001:  x-Big5-HKSCS-2001
102 x-Big5-Solaris:  x-Big5-Solaris
103 x-euc-jp-linux:  x-euc-jp-linux
104 x-EUC-TW:  x-EUC-TW
105 x-eucJP-Open:  x-eucJP-Open
106 x-IBM1006:  x-IBM1006
107 x-IBM1025:  x-IBM1025
108 x-IBM1046:  x-IBM1046
109 x-IBM1097:  x-IBM1097
110 x-IBM1098:  x-IBM1098
111 x-IBM1112:  x-IBM1112
112 x-IBM1122:  x-IBM1122
113 x-IBM1123:  x-IBM1123
114 x-IBM1124:  x-IBM1124
115 x-IBM1364:  x-IBM1364
116 x-IBM1381:  x-IBM1381
117 x-IBM1383:  x-IBM1383
118 x-IBM300:  x-IBM300
119 x-IBM33722:  x-IBM33722
120 x-IBM737:  x-IBM737
121 x-IBM833:  x-IBM833
122 x-IBM834:  x-IBM834
123 x-IBM856:  x-IBM856
124 x-IBM874:  x-IBM874
125 x-IBM875:  x-IBM875
126 x-IBM921:  x-IBM921
127 x-IBM922:  x-IBM922
128 x-IBM930:  x-IBM930
129 x-IBM933:  x-IBM933
130 x-IBM935:  x-IBM935
131 x-IBM937:  x-IBM937
132 x-IBM939:  x-IBM939
133 x-IBM942:  x-IBM942
134 x-IBM942C:  x-IBM942C
135 x-IBM943:  x-IBM943
136 x-IBM943C:  x-IBM943C
137 x-IBM948:  x-IBM948
138 x-IBM949:  x-IBM949
139 x-IBM949C:  x-IBM949C
140 x-IBM950:  x-IBM950
141 x-IBM964:  x-IBM964
142 x-IBM970:  x-IBM970
143 x-ISCII91:  x-ISCII91
144 x-ISO-2022-CN-CNS:  x-ISO-2022-CN-CNS
145 x-ISO-2022-CN-GB:  x-ISO-2022-CN-GB
146 x-iso-8859-11:  x-iso-8859-11
147 x-JIS0208:  x-JIS0208
148 x-JISAutoDetect:  x-JISAutoDetect
149 x-Johab:  x-Johab
150 x-MacArabic:  x-MacArabic
151 x-MacCentralEurope:  x-MacCentralEurope
152 x-MacCroatian:  x-MacCroatian
153 x-MacCyrillic:  x-MacCyrillic
154 x-MacDingbat:  x-MacDingbat
155 x-MacGreek:  x-MacGreek
156 x-MacHebrew:  x-MacHebrew
157 x-MacIceland:  x-MacIceland
158 x-MacRoman:  x-MacRoman
159 x-MacRomania:  x-MacRomania
160 x-MacSymbol:  x-MacSymbol
161 x-MacThai:  x-MacThai
162 x-MacTurkish:  x-MacTurkish
163 x-MacUkraine:  x-MacUkraine
164 x-MS932_0213:  x-MS932_0213
165 x-MS950-HKSCS:  x-MS950-HKSCS
166 x-MS950-HKSCS-XP:  x-MS950-HKSCS-XP
167 x-mswin-936:  x-mswin-936
168 x-PCK:  x-PCK
169 x-SJIS_0213:  x-SJIS_0213
170 x-UTF-16LE-BOM:  x-UTF-16LE-BOM
171 X-UTF-32BE-BOM:  X-UTF-32BE-BOM
172 X-UTF-32LE-BOM:  X-UTF-32LE-BOM
173 x-windows-50220:  x-windows-50220
174 x-windows-50221:  x-windows-50221
175 x-windows-874:  x-windows-874
176 x-windows-949:  x-windows-949
177 x-windows-950:  x-windows-950
178 x-windows-iso2022jp:  x-windows-iso2022jp
View Code

 

2、Java中的幾種編碼格式

2.1、文件、項目、工作空間的編碼格式

默認是【文件編碼格式】繼承於【項目編碼格式】繼承於【工作空間編碼格式】繼承於【系統編碼格式】。都可以自己設置。 

以eclipse里為例:

    • 【工作空間編碼格式】設置:在Winodws->Preferences->General->Workspace里設置Text file encoding,默認繼承於【系統編碼格式】如簡體中文環境下是GBK。這樣每次新建工程默認都會以此編碼
    • 【項目編碼格式】設置:在項目右鍵->Source里設置Text file encoding,默認繼承於【工作空間編碼格式】。這樣在項目下新建文件如java源文件會以此編碼。
    • 【文件編碼格式】設置:在文件右鍵->Source里設置Text file encoding,默認繼承於【項目編碼格式】。

  值得注意的是,若在eclipse里保存了.java文件后又用上述方式修改了該文件的編碼方式,則可能會出現亂碼,並且新輸入的代碼可能也保存不了,提示輸入的有些字符無法用當前編碼方式映射保存。亂碼原因是用新指定的編碼格式來解碼原來格式編碼成的文件,保存不了原因是新輸入的有些字符如中文無法用新編碼格式如ISO8859-1編碼。所以要修改已寫好的.java文件的編碼方式,為避免亂碼等問題可以在文本編輯器中打開並另存為其他格式。

2.2、運行環境的編碼格式

和外部進行數據交換(如讀文件時)的時候所用的編碼格式。可以通過 Charset.defaultCharset().name() 查看默認編碼格式,繼承於【文件編碼格式】。

 Charset.defaultCharset()方法如下:可見 Charset.defaultCharset()優先采用 [調用此方法(直接或間接調用)的main函數所在的文件的] file.encoding,無法確認的話用UTF-8

 1     public static Charset defaultCharset() {
 2         if (defaultCharset == null) {
 3             synchronized (Charset.class) {
 4                 String csn = AccessController.doPrivileged(
 5                     new GetPropertyAction("file.encoding"));
 6                 Charset cs = lookup(csn);
 7                 if (cs != null)
 8                     defaultCharset = cs;
 9                 else
10                     defaultCharset = forName("UTF-8");
11             }
12         }
13         return defaultCharset;
14     }

需要注意的是,上面的file.encoding指的是main函數所在文件的編碼方式,而不是Charset.defaultCharset()代碼所在文件的編碼方式。如有file1.java、file2.java兩個文件,其編碼方式不同,file1.java有個靜態方法getFileEncoding()返回Charset.defaultCharset(),則file2.java里調用file1.getFileEncoding()得到的是file2.java的編碼方式。

另,可在程序里獲取file.encoding等system properties:

Properties prop = System.getProperties();
Iterator it = prop.keySet().iterator();
while (it.hasNext()) {
    String key = it.next().toString();
    System.out.println(key + ":           " + prop.getProperty(key));
}

結果中有幾個值得注意的屬性:

file.encoding:                  UTF-16 //即前面提到的file.encoding
sun.jnu.encoding:               GBK //網上說與文件命名、命令行參數等有關
sun.io.unicode.encoding:        UnicodeLittle //應該是Java內部編碼,UTF-16L
sun.cpu.endian:                 little //CPU為小端

 這里的file.encoding指的是main函數所在文件的編碼方式。

2.3、內部的編碼格式

Unicode-16編碼,是Java程序內部所用的編碼格式。所有外部字符內容在Java 程序內全部轉換成此編碼格式。

.java文件中的字符串等(3.1)、外部文件、網絡資源、數據庫資源等讀入后轉為內部編碼格式的數據;寫出時從內部編碼格式轉為指定格式的數據;字符串在內存中也可按指定編碼格式進行轉換(3.2)。詳見章節3。

 

3、Java中的數據編解碼轉換

.class文件:UTF-8格式

JVM內部:UTF-16格式

解碼

讀:從外部讀入(轉換為Java內部編碼UTF-16):read;用於顯示:文本編輯器打開顯示

解碼成字符(串):new string(byte[])

編碼

寫:用於保存或輸出到外部(與字符流相關的才需編碼,將字符按編碼方式轉成字節流后輸出;若是字節流則直接輸出,無需編碼)或與外界(網絡、數據庫等)交互:與字符相關的write,如InputStreamReader、OutputStreamWriter、BufferedWriter等

轉變成字節:String.getBytes()

字符(串)與byte[]之間轉換時需要提供編碼格式參數(然而很多時候我們沒提供,其實此時采用了默認格式,即file.encoding)

String值始終是以內部編碼格式(UTF-16)存儲

3.1、默認轉換:Java源文件到.class文件到內存

即編譯器根據.java文件的編碼格式讀出其中的內容並編譯成UTF-8格式的.class文件,包括字符串等;運行時JVM按UTF-8格式讀取.class文件到內存中。

    • 保存:保存.java文件時,以file.encoding將文件內容編碼成二進制存入文件。 
    • 編譯:運行javac時若沒有用-encoding參數指定源文件的編碼方式,則以默認格式Charset.defaultCharset()即file.encoding來解碼.java文件並編譯成UTF-8格式.class文件。
    • 運行:運行Java程序時JVM以UTF-8格式解碼.class文件並讀入到內存中,此時包括字符串等都以UTF-16格式存在於內存中了。

上述過程一般不需用戶干預,除非用戶指定其他參數否則可以看出其就是用file.encoding格式來讀.java文件並轉成內部UTF-16格式的數據,因此這些默認轉換一般不會出錯。

3.2、運行中轉換:網絡文件、本地文件、數據庫中的內容等

3.2.1、讀入

    • 從本地文件中讀到內存:按字節讀流的話結果(byte[])與程序運行環境的編碼格式方式無關,只與文件的編碼方式有關;按字符(串)讀則與運行環境采用什么編碼格式有關,需要將之指定為本地文件的編碼方式或父集。這樣在讀時會以指定編碼格式來解碼被讀取的文件,轉變成內部的UTF-16格式於內存中。
    • 從網絡文件中讀到內存:與上類似。對於與瀏覽器交互的程序,如Servlet、JSP(也是轉換成Servlet放在Web容器臨時目錄)等,若沒有指定編碼方式,則默認以ISO8859-1解碼接收到的參數、編碼返回的數據給瀏覽器。過程圖如下:

 

    • 從數據庫中讀取到內存:與上類似,只不過默認編碼格式也為8859-1。對於幾乎所有數據庫的JDBC驅動程序,默認的在JAVA程序和數據庫之間傳遞數據都是以ISO-8859-1為默認編碼格式的,所以,我們的程序在向數據庫內存儲包含中文的數據時,JDBC首先是把程序內部的UNICODE編碼格式的數據轉化為ISO-8859-1的格式,然后傳遞到數據庫中,在數據庫保存數據時,它默認即以ISO-8859-1保存。所以,這是為什么我們常常在數據庫中讀出的中文數據是亂碼。 過程圖如下:

3.2.2、寫出

    • 從內存中寫入到文件:將內存中UTF-16格式的內容 編碼成 程序運行環境的編格式的內容,寫出到文件。
    • 內存中轉換:
      • 內存中數據編碼格式轉換:轉換時一般都要指定某種編碼格式,未指定的話采用Charset.defaultCharset()即file.encoding
        • String轉byte[]如getBytes():Java內部編碼格式(UTF-16)的數據轉換成指定格式的數據
        • byte[]轉String:以指定格式將byte[]數據解碼成字符串
        • 其他

3.2.3、console交互:(包含上述的讀和寫過程)

    • 這種情況,運行該類首先需要JVM支持,即操作系統中必須安裝有JRE。運行過程是這樣的:首先java啟動JVM,此時JVM讀出操作系統中保存的class文件並把內容讀入內存中,此時內存中為UNICODE格式的class類,然后JVM運行它,如果此時此類需要接收用戶輸入,則類會默認用file.encoding編碼格式對用戶輸入的串進行編碼並轉化為unicode保存入內存(用戶可以設置輸入流的編碼格式)。程序運行后,產生的字符串(UNICODE編碼的)再回交給JVM,最后JRE把此字符串再轉化為file.encoding格式(用戶可以設置輸出流的編碼格式)傳遞給操作系統顯示接口並輸出到界面上。 過程圖如下:

 

 

以 下面代碼為例,涉及到多次編解碼轉換(假定文件以UTF-8編碼即file.encoding為UTF-8):

        FileWriter fw = new FileWriter("文件1.txt");
        fw.write(new String("abc".getBytes("GBK"),"UTF-16"));
        fw.close();
  1. 默認轉換
    • 編碼:保存.java源文件時,"abc"在文件中以file.encoding的格式編碼成二進制存着。
    • 解碼編碼:編譯時,編譯器以file.encoding格式解碼.java文件並編譯成Java內部編碼格式即UTF-16格式的.class文件。
    • 解碼:程序運行時,JVM加載.class文件,以內部編碼格式UTF-16解碼.class文件,故"abc"以此格式存在內存中。
  2. 程序中轉換
    • 編碼:"abc".getBytes("GBK")將內存中UTF-16格式的"abc"編碼成GBK格式,得到字節數組。
    • 解碼:new String(bytes,"UTF-16"))將上步的GBK格式的字節數組解碼成UTF-16的字符串。
    • 編碼:fw.write()以FileWriter的默認編碼方式Charset.defaultCharset()將上步的String編碼成二進制寫到文件里,當然也可以指定所用的編碼方式。所以要正確顯示文件里的內容,需要以寫入時的相應編碼方式來解碼打開。

 另附一張不是很貼切的圖,關於String、char[]、byte[]之間的轉換:

 

4、參考資料

[1]、http://www.voidcn.com/blog/u013678930/article/p-6166506.html

[2]、http://www.iteye.com/topic/112049

[3]、http://1035054540-qq-com.iteye.com/blog/1856060

[4]、http://www.bianceng.cn/java/j149.htm


免責聲明!

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



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