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
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[]數據解碼成字符串
- 其他
- 內存中數據編碼格式轉換:轉換時一般都要指定某種編碼格式,未指定的話采用Charset.defaultCharset()即file.encoding
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();
- 默認轉換
- 編碼:保存.java源文件時,"abc"在文件中以file.encoding的格式編碼成二進制存着。
- 解碼編碼:編譯時,編譯器以file.encoding格式解碼.java文件並編譯成Java內部編碼格式即UTF-16格式的.class文件。
- 解碼:程序運行時,JVM加載.class文件,以內部編碼格式UTF-16解碼.class文件,故"abc"以此格式存在內存中。
- 程序中轉換
- 編碼:"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