寫于:2002/07 最后更新: 02/22/2006 14:42:55
版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
http://www.chedong.com/tech/hello_unicode.html
關鍵詞:linux java mutlibyte encoding locale i18n i10n chinese? ISO-8859-1 GB2312 BIG5 GBK UNICODE
內容摘要:
不知道你有沒有這樣的感受:為什么PHP很少有亂碼問題而用Java做WEB應用卻這么麻煩呢?為什么在Google上能用簡體中文查到繁體中文,甚至日文的結果?而且用Google的時候發現它居然能自動根據我使用瀏覽器的語言選擇自動調出中文界面?
很多國際化應用的讓我理解了這么一個道理:Unicode是為更方便的做國際化應用設計的,而Java核心的字符是基于UNICODE的,這一機制為應用提供了對中文“字”的控制(而不是字節)。但如果不仔細理解其中的規范,這種自由反而會成為累贅,從而導致更多的亂碼問題:
-
關于字符集的一些基本概念;
-
試驗1:顯示系統的環境設置和支持的編碼方式;
-
試驗2:系統缺省編碼方式對Java應用的輸入輸出影響;
-
試驗3:在WEB應用中輸出和輸出中的字符集問題;
注意:以下說明不是嚴格定義,一些比喻僅作為方便理解使用。
假設一個字符就是棋盤上的一個棋子,有其固定的坐標,如果需要區別所有的字符,就需要有足夠的棋格容納不同的“字符”。
英文和歐洲其他語言的單字節字符集
(SingleByte Charsets):
首先對于ISO-8859系列的字符集都想象成一個:2^8 = 16 * 16 = 256個格子的棋盤,這樣所有的西文字符(英文)用這樣一個16×16的坐標系就基本可以覆蓋全了。而英文實際上只用其中小于128(\x80)的部分就夠了。利用大于128部分的空間的不同定義規則形成了真對其他歐洲語言的擴展字符集:ISO-8859-2 ISO-8859-4等……
ISO-8859-1
|
ISO-8859-7
|
其他語言
|
|
|
|
GB2312 BIG5 SJIS等多字節字符集(MultiByte Charsets):
對于亞洲語言來說:漢字這么多,用這么一個256格的小棋盤肯定放不下,所以要區別成千上萬的漢字解決辦法就是用2個字節(坐標)來定位一個“字”在棋盤上的位置,將以上規則做一個擴展:
- 如果第1個字符是小于128(\x80)的仍和英文字符集編碼方式保持兼容;
- 如果第1個字符是大于128(\x80)的,就當成是漢字的第1個字節,這個自己和后面緊跟的1個字節組成一個漢字;
其結果相當于在位于128以上的小棋格里每個小棋格又劃分出了一個16×16的小棋盤。這樣一個棋盤中的格子數(可能容納的字符數)就變成了128 + 128 * 256。按照類似的方式有了簡體中文的GB2312標準,繁體中文的BIG5字符集和日文的SJIS字符集等,GB2312字符集包含大約有六仟多個常用簡體漢字。
由此可以看出,所有這些從ASCII擴展式的編碼方式中:英文部分都是兼容的,但擴展部分的編碼方式是不兼容的,雖然很多字在3種體系中寫法一致(比如“中文”這2個字)但在相應字符集中的坐標不一致,所以GB2312編寫的頁面用BIG5看就變得面目全非了。而且有時候經常在瀏覽其他非英語國家的頁面時(比如包含有德語的人名時)經常出現奇怪的漢字,其實就是擴展位的編碼沖突造成的。
我把GBK和GB18030理解成一個小UNICODE:GBK字符集是GB2312的擴展(K),GBK里大約有貳萬玖仟多個字符,除了保持和GB2312兼容外,繁體中文字,甚至連日文的假名字符也能顯示。而GB18030-2000則是一個更復雜的字符集,采用變長字節的編碼方式,能夠支持更多的字符。關于漢字的編碼方式比較詳細的定義規范可以參考:
http://www.unihan.com.cn/cjk/ana17.htm
ASCII(英文) ==> 西歐文字 ==> 東歐字符集(俄文,希臘語等) ==> 東亞字符集(GB2312 BIG5 SJIS等)==> 擴展字符集GBK GB18030這個發展過程基本上也反映了字符集標準的發展過程,但這么隨著時間的推移,尤其是互聯網讓跨語言的信息的交互變得越來越多的時候,太多多針對本地語言的編碼標準的出現導致一個應用程序的國際化變得成本非常高。尤其是你要編寫一個同時包含法文和簡體中文的文檔,這時候一般都會想到要是用一個通用的字符集能夠顯示所有語言的所有文字就好了,而且這樣做應用也能夠比較方便的國際化,為了達到這個目標,即使應用犧牲一些空間和程序效率也是非常值得的。UNICODE就是這樣一個通用的解決方案。
UNICODE雙字節字符集
所以你可以把UNICODE想象成這樣:讓所有的字符(包括英文)都用2個字節(2個8位)表示,這樣就有了一個2^(8*2) = 256 * 256 = 65536個格子的大棋盤。在這個棋盤中,這樣中(簡繁)日韓(還包括越南)文字作為CJK字符集都放在一定的區位內,為了減少重復,各種語言中寫法一樣的字共享一個“棋格”。詳細的區位見附錄A
Unicode:(DoubleByte Charsets)
什么還要有UTF-8?畢竟互聯網70%以上的信息仍然是英文。如果連英文都用2個字節存取(UCS-2),空間浪費不就太多了?所謂UTF-8就是這樣一個為了提高英文存取效率的字符集轉換格式:Unicode Transformation Form 8-bit form。用UTF-8,UNICODE的2字節字符用變長個(1-3個字節)表示:
- 對英文,仍然和ASCII一樣用1個字節表示,這個字節的值小于128(\x80);
- 對其他語言的用一個值位于128-256之間的字節開始,再加后面緊跟的2個字節表示,一個字符一共是3個字節;
因此,在應用中程序處理過程中所有字符都是16位(雙字節),但在存取轉換成字節流時使用UTF-8格式轉換,對于英文字符來說和原來用ASCII方式存取時相比大小仍然是一樣的,而對中文來說和原來的GB2312編碼方式相比,大小為:(3字節/2字節)=1.5倍
小節:
假設英文字符集是一個16×16的棋盤,么其他語言的字符集就是把高位區重新分割的(> 128)的中等棋盤,多種字符集之間互不兼容而UNICODE本身就相當于一個256×256的大棋盤,通過一定規則將英文和其他所有語言的字符都包含在內。
為了了解Java應用的編碼處理的機制,首先要了解操作系統對JVM缺省編碼方式的影響,因此我做了一個Env.java,用于打印顯示不同系統下JVM的屬性和系統支持的LOCALE。程序很簡單:
/*
* Copyright (c) 2002 Email: chedongATbigfoot.com/chedongATchedong.com
* $Id: hello_unicode.html,v 1.6 2003/11/09 07:57:11 chedong Exp $
*/
import java.util.*;
import java.text.*;
/**
* 目的:
* 顯示環境變量和JVM的缺省屬性
* 輸入:無
* 輸出:
* 1 支持的LOCALE
* 2 JVM的缺省屬性
*/
public class Env {
/**
* main entrance
*/
public static void main(String[] args) {
System.out.println("Hello, it's: " + new Date());
//print available locales
Locale list[] = DateFormat.getAvailableLocales();
System.out.println("======System available locales:======== ");
for (int i = 0; i < list.length; i++) {
System.out.println(list[i].toString() + "\t" + list[i].getDisplayName());
}
//print JVM default properties
System.out.println("======System property======== ");
System.getProperties().list(System.out);
}
}
最需要注意的是JVM的file.encoding屬性,這個屬性確定了JVM的缺省的編碼/解碼方式:從而影響應用中所有字節流==>字符流的解碼方式?,字符流==>字節流的編碼方式。
??? LINUX下的LOCALE可以通過 LANG=zh_CN; LC_ALL=zh_CN.GBK; export LANG LC_ALL 設置。locale 命令可以顯示系統當前的環境設置
??? Windows的LOCALE可以通過 控制面板==>區域設置 設置實現
GNU/Linux 2.4.x (J2SE1.3.1) LANG=en_US LC_ALL=en_US
|
GNU/Linux 2.4.x (J2SE1.3.1) LANG=zh_CN LC_ALL=zh_CN.GBK
|
Windows 2000(J2SE1.3.0)? 區域設置:中國? 中文
|
Windows 2000(J2SE1.3.0)? 區域設置:英國 英文
|
Hello, it's: Tue Jul 30 11:05:44 CST 2002 ======System available locales:======== en English en_US English (United States) ar Arabic ar_AE Arabic (United Arab Emirates) ar_BH Arabic (Bahrain) ar_DZ Arabic (Algeria) ar_EG Arabic (Egypt) ar_IQ Arabic (Iraq) ar_JO Arabic (Jordan) ar_KW Arabic (Kuwait) ar_LB Arabic (Lebanon) ar_LY Arabic (Libya) ar_MA Arabic (Morocco) ar_OM Arabic (Oman) ar_QA Arabic (Qatar) ar_SA Arabic (Saudi Arabia) ar_SD Arabic (Sudan) ar_SY Arabic (Syria) ar_TN Arabic (Tunisia) ar_YE Arabic (Yemen) be Byelorussian be_BY Byelorussian (Belarus) bg Bulgarian bg_BG Bulgarian (Bulgaria) ca Catalan ca_ES Catalan (Spain) ca_ES_EURO Catalan (Spain,Euro) cs Czech cs_CZ Czech (Czech Republic) da Danish da_DK Danish (Denmark) de German de_AT German (Austria) de_AT_EURO German (Austria,Euro) de_CH German (Switzerland) de_DE German (Germany) de_DE_EURO German (Germany,Euro) de_LU German (Luxembourg) de_LU_EURO German (Luxembourg,Euro) el Greek el_GR Greek (Greece) en_AU English (Australia) en_CA English (Canada) en_GB English (United Kingdom) en_IE English (Ireland) en_IE_EURO English (Ireland,Euro) en_NZ English (New Zealand) en_ZA English (South Africa) es Spanish es_BO Spanish (Bolivia) es_AR Spanish (Argentina) es_CL Spanish (Chile) es_CO Spanish (Colombia) es_CR Spanish (Costa Rica) es_DO Spanish (Dominican Republic) es_EC Spanish (Ecuador) es_ES Spanish (Spain) es_ES_EURO Spanish (Spain,Euro) es_GT Spanish (Guatemala) es_HN Spanish (Honduras) es_MX Spanish (Mexico) es_NI Spanish (Nicaragua) et Estonian es_PA Spanish (Panama) es_PE Spanish (Peru) es_PR Spanish (Puerto Rico) es_PY Spanish (Paraguay) es_SV Spanish (El Salvador) es_UY Spanish (Uruguay) es_VE Spanish (Venezuela) et_EE Estonian (Estonia) fi Finnish fi_FI Finnish (Finland) fi_FI_EURO Finnish (Finland,Euro) fr French fr_BE French (Belgium) fr_BE_EURO French (Belgium,Euro) fr_CA French (Canada) fr_CH French (Switzerland) fr_FR French (France) fr_FR_EURO French (France,Euro) fr_LU French (Luxembourg) fr_LU_EURO French (Luxembourg,Euro) hr Croatian hr_HR Croatian (Croatia) hu Hungarian hu_HU Hungarian (Hungary) is Icelandic is_IS Icelandic (Iceland) it Italian it_CH Italian (Switzerland) it_IT Italian (Italy) it_IT_EURO Italian (Italy,Euro) iw Hebrew iw_IL Hebrew (Israel) ja Japanese ja_JP Japanese (Japan) ko Korean ko_KR Korean (South Korea) lt Lithuanian lt_LT Lithuanian (Lithuania) lv Latvian (Lettish) lv_LV Latvian (Lettish) (Latvia) mk Macedonian mk_MK Macedonian (Macedonia) nl Dutch nl_BE Dutch (Belgium) nl_BE_EURO Dutch (Belgium,Euro) nl_NL Dutch (Netherlands) nl_NL_EURO Dutch (Netherlands,Euro) no Norwegian no_NO Norwegian (Norway) no_NO_NY Norwegian (Norway,Nynorsk) pl Polish pl_PL Polish (Poland) pt Portuguese pt_BR Portuguese (Brazil) pt_PT Portuguese (Portugal) pt_PT_EURO Portuguese (Portugal,Euro) ro Romanian ro_RO Romanian (Romania) ru Russian ru_RU Russian (Russia) sh Serbo-Croatian sh_YU Serbo-Croatian (Yugoslavia) sk Slovak sk_SK Slovak (Slovakia) sl Slovenian sl_SI Slovenian (Slovenia) sq Albanian sq_AL Albanian (Albania) sr Serbian sr_YU Serbian (Yugoslavia) sv Swedish sv_SE Swedish (Sweden) th Thai th_TH Thai (Thailand) tr Turkish tr_TR Turkish (Turkey) uk Ukrainian uk_UA Ukrainian (Ukraine) zh Chinese zh_CN Chinese (China) zh_HK Chinese (Hong Kong) zh_TW Chinese (Taiwan) ======System property======== -- listing properties -- java.runtime.name=Java(TM) 2 Runtime Environment, Stand... sun.boot.library.path=/usr/java/jdk1.3.1_04/jre/lib/i386 java.vm.version=1.3.1_04-b02 java.vm.vendor=Sun Microsystems Inc. java.vendor.url=http://java.sun.com/ path.separator=: java.vm.name=Java HotSpot(TM) Client VM file.encoding.pkg=sun.io java.vm.specification.name=Java Virtual Machine Specification user.dir=/home/chedong/src/char_test java.runtime.version=1.3.1_04-b02 java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment os.arch=i386 java.io.tmpdir=/tmp line.separator=
java.vm.specification.vendor=Sun Microsystems Inc. java.awt.fonts= os.name=Linux java.library.path=/usr/java/jdk1.3.1_04/jre/lib/i386:/u... java.specification.name=Java Platform API Specification java.class.version=47.0 os.version=2.4.7-10 user.home=/home/chedong user.timezone=Asia/Shanghai java.awt.printerjob=sun.awt.motif.PSPrinterJob file.encoding=ISO-8859-1
java.specification.version=1.3
user.name=chedong
java.class.path=/home/chedong/classes
java.vm.specification.version=1.0
java.home=/usr/java/jdk1.3.1_04/jre
user.language=en
java.specification.vendor=Sun Microsystems Inc.
java.vm.info=mixed mode
java.version=1.3.1_04
java.ext.dirs=/usr/java/jdk1.3.1_04/jre/lib/ext
sun.boot.class.path=/usr/java/jdk1.3.1_04/jre/lib/rt.jar:...
java.vendor=Sun Microsystems Inc.
file.separator=/
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
user.region=US
sun.cpu.isalist=
|
Hello, it's: Tue Jul 30 11:07:34 CST 2002 ======System available locales:======== en 英文 en_US 英文 (美國) ar 阿拉伯文 ar_AE 阿拉伯文 (阿拉伯聯合酋長國) ar_BH 阿拉伯文 (巴林) ar_DZ 阿拉伯文 (阿爾及利亞) ar_EG 阿拉伯文 (埃及) ar_IQ 阿拉伯文 (伊拉克) ar_JO 阿拉伯文 (約旦) ar_KW 阿拉伯文 (科威特) ar_LB 阿拉伯文 (黎巴嫩) ar_LY 阿拉伯文 (利比亞) ar_MA 阿拉伯文 (摩洛哥) ar_OM 阿拉伯文 (阿曼) ar_QA 阿拉伯文 (卡塔爾) ar_SA 阿拉伯文 (沙特阿拉伯) ar_SD 阿拉伯文 (蘇丹) ar_SY 阿拉伯文 (敘利亞) ar_TN 阿拉伯文 (突尼斯) ar_YE 阿拉伯文 (也門) be 白俄羅斯文 be_BY 白俄羅斯文 (白俄羅斯) bg 保加利亞文 bg_BG 保加利亞文 (保加利亞) ca 加泰羅尼亞文 ca_ES 加泰羅尼亞文 (西班牙) ca_ES_EURO 加泰羅尼亞文 (西班牙,Euro) cs 捷克文 cs_CZ 捷克文 (捷克共和國) da 丹麥文 da_DK 丹麥文 (丹麥) de 德文 de_AT 德文 (奧地利) de_AT_EURO 德文 (奧地利,Euro) de_CH 德文 (瑞士) de_DE 德文 (德國) de_DE_EURO 德文 (德國,Euro) de_LU 德文 (盧森堡) de_LU_EURO 德文 (盧森堡,Euro) el 希臘文 el_GR 希臘文 (希臘) en_AU 英文 (澳大利亞) en_CA 英文 (加拿大) en_GB 英文 (英國) en_IE 英文 (愛爾蘭) en_IE_EURO 英文 (愛爾蘭,Euro) en_NZ 英文 (新西蘭) en_ZA 英文 (南非) es 西班牙文 es_BO 西班牙文 (玻利維亞) es_AR 西班牙文 (阿根廷) es_CL 西班牙文 (智利) es_CO 西班牙文 (哥倫比亞) es_CR 西班牙文 (哥斯達黎加) es_DO 西班牙文 (多米尼加共和國) es_EC 西班牙文 (厄瓜多爾) es_ES 西班牙文 (西班牙) es_ES_EURO 西班牙文 (西班牙,Euro) es_GT 西班牙文 (危地馬拉) es_HN 西班牙文 (洪都拉斯) es_MX 西班牙文 (墨西哥) es_NI 西班牙文 (尼加拉瓜) et 愛沙尼亞文 es_PA 西班牙文 (巴拿馬) es_PE 西班牙文 (秘魯) es_PR 西班牙文 (波多黎哥) es_PY 西班牙文 (巴拉圭) es_SV 西班牙文 (薩爾瓦多) es_UY 西班牙文 (烏拉圭) es_VE 西班牙文 (委內瑞拉) et_EE 愛沙尼亞文 (愛沙尼亞) fi 芬蘭文 fi_FI 芬蘭文 (芬蘭) fi_FI_EURO 芬蘭文 (芬蘭,Euro) fr 法文 fr_BE 法文 (比利時) fr_BE_EURO 法文 (比利時,Euro) fr_CA 法文 (加拿大) fr_CH 法文 (瑞士) fr_FR 法文 (法國) fr_FR_EURO 法文 (法國,Euro) fr_LU 法文 (盧森堡) fr_LU_EURO 法文 (盧森堡,Euro) hr 克羅地亞文 hr_HR 克羅地亞文 (克羅地亞) hu 匈牙利文 hu_HU 匈牙利文 (匈牙利) is 冰島文 is_IS 冰島文 (冰島) it 意大利文 it_CH 意大利文 (瑞士) it_IT 意大利文 (意大利) it_IT_EURO 意大利文 (意大利,Euro) iw 希伯來文 iw_IL 希伯來文 (以色列) ja 日文 ja_JP 日文 (日本) ko 朝鮮文 ko_KR 朝鮮文 (南朝鮮) lt 立陶宛文 lt_LT 立陶宛文 (立陶宛) lv 拉托維亞文(列托) lv_LV 拉托維亞文(列托) (拉脫維亞) mk 馬其頓文 mk_MK 馬其頓文 (馬其頓王國) nl 荷蘭文 nl_BE 荷蘭文 (比利時) nl_BE_EURO 荷蘭文 (比利時,Euro) nl_NL 荷蘭文 (荷蘭) nl_NL_EURO 荷蘭文 (荷蘭,Euro) no 挪威文 no_NO 挪威文 (挪威) no_NO_NY 挪威文 (挪威,Nynorsk) pl 波蘭文 pl_PL 波蘭文 (波蘭) pt 葡萄牙文 pt_BR 葡萄牙文 (巴西) pt_PT 葡萄牙文 (葡萄牙) pt_PT_EURO 葡萄牙文 (葡萄牙,Euro) ro 羅馬尼亞文 ro_RO 羅馬尼亞文 (羅馬尼亞) ru 俄文 ru_RU 俄文 (俄羅斯) sh 塞波尼斯-克羅地亞文 sh_YU 塞波尼斯-克羅地亞文 (南斯拉夫) sk 斯洛伐克文 sk_SK 斯洛伐克文 (斯洛伐克) sl 斯洛文尼亞文 sl_SI 斯洛文尼亞文 (斯洛文尼亞) sq 阿爾巴尼亞文 sq_AL 阿爾巴尼亞文 (阿爾巴尼亞) sr 塞爾維亞文 sr_YU 塞爾維亞文 (南斯拉夫) sv 瑞典文 sv_SE 瑞典文 (瑞典) th 泰文 th_TH 泰文 (泰國) tr 土耳其文 tr_TR 土耳其文 (土耳其) uk 烏克蘭文 uk_UA 烏克蘭文 (烏克蘭) zh 中文 zh_CN 中文 (中國) zh_HK 中文 (香港) zh_TW 中文 (臺灣) ======System property======== -- listing properties -- java.runtime.name=Java(TM) 2 Runtime Environment, Stand... sun.boot.library.path=/usr/java/jdk1.3.1_04/jre/lib/i386 java.vm.version=1.3.1_04-b02 java.vm.vendor=Sun Microsystems Inc. java.vendor.url=http://java.sun.com/ path.separator=: java.vm.name=Java HotSpot(TM) Client VM file.encoding.pkg=sun.io java.vm.specification.name=Java Virtual Machine Specification user.dir=/home/chedong/src/char_test java.runtime.version=1.3.1_04-b02 java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment os.arch=i386 java.io.tmpdir=/tmp line.separator=
java.vm.specification.vendor=Sun Microsystems Inc. java.awt.fonts= os.name=Linux java.library.path=/usr/java/jdk1.3.1_04/jre/lib/i386:/u... java.specification.name=Java Platform API Specification java.class.version=47.0 os.version=2.4.7-10 user.home=/home/chedong user.timezone=Asia/Shanghai java.awt.printerjob=sun.awt.motif.PSPrinterJob file.encoding=GBK
java.specification.version=1.3
user.name=chedong
java.class.path=/home/chedong/classes
java.vm.specification.version=1.0
java.home=/usr/java/jdk1.3.1_04/jre
user.language=zh
java.specification.vendor=Sun Microsystems Inc.
java.vm.info=mixed mode
java.version=1.3.1_04
java.ext.dirs=/usr/java/jdk1.3.1_04/jre/lib/ext
sun.boot.class.path=/usr/java/jdk1.3.1_04/jre/lib/rt.jar:...
java.vendor=Sun Microsystems Inc.
file.separator=/
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
user.region=CN
sun.cpu.isalist=
|
Hello, it's: Tue Jul 30 11:49:36 CST 2002 ======System available locales:======== en English en_US English (United States) ar Arabic ar_AE Arabic (United Arab Emirates) ar_BH Arabic (Bahrain) ar_DZ Arabic (Algeria) ar_EG Arabic (Egypt) ar_IQ Arabic (Iraq) ar_JO Arabic (Jordan) ar_KW Arabic (Kuwait) ar_LB Arabic (Lebanon) ar_LY Arabic (Libya) ar_MA Arabic (Morocco) ar_OM Arabic (Oman) ar_QA Arabic (Qatar) ar_SA Arabic (Saudi Arabia) ar_SD Arabic (Sudan) ar_SY Arabic (Syria) ar_TN Arabic (Tunisia) ar_YE Arabic (Yemen) be Byelorussian be_BY Byelorussian (Belarus) bg Bulgarian bg_BG Bulgarian (Bulgaria) ca Catalan ca_ES Catalan (Spain) ca_ES_EURO Catalan (Spain,Euro) cs Czech cs_CZ Czech (Czech Republic) da Danish da_DK Danish (Denmark) de German de_AT German (Austria) de_AT_EURO German (Austria,Euro) de_CH German (Switzerland) de_DE German (Germany) de_DE_EURO German (Germany,Euro) de_LU German (Luxembourg) de_LU_EURO German (Luxembourg,Euro) el Greek el_GR Greek (Greece) en_AU English (Australia) en_CA English (Canada) en_GB English (United Kingdom) en_IE English (Ireland) en_IE_EURO English (Ireland,Euro) en_NZ English (New Zealand) en_ZA English (South Africa) es Spanish es_AR Spanish (Argentina) es_BO Spanish (Bolivia) es_CL Spanish (Chile) es_CO Spanish (Colombia) es_CR Spanish (Costa Rica) es_DO Spanish (Dominican Republic) es_EC Spanish (Ecuador) es_ES Spanish (Spain) es_ES_EURO Spanish (Spain,Euro) es_GT Spanish (Guatemala) es_HN Spanish (Honduras) es_MX Spanish (Mexico) es_NI Spanish (Nicaragua) es_PA Spanish (Panama) es_PE Spanish (Peru) es_PR Spanish (Puerto Rico) es_PY Spanish (Paraguay) es_SV Spanish (El Salvador) es_UY Spanish (Uruguay) es_VE Spanish (Venezuela) et Estonian et_EE Estonian (Estonia) fi Finnish fi_FI Finnish (Finland) fi_FI_EURO Finnish (Finland,Euro) fr French fr_BE French (Belgium) fr_BE_EURO French (Belgium,Euro) fr_CA French (Canada) fr_CH French (Switzerland) fr_FR French (France) fr_FR_EURO French (France,Euro) fr_LU French (Luxembourg) fr_LU_EURO French (Luxembourg,Euro) hr Croatian hr_HR Croatian (Croatia) hu Hungarian hu_HU Hungarian (Hungary) is Icelandic is_IS Icelandic (Iceland) it Italian it_CH Italian (Switzerland) it_IT Italian (Italy) it_IT_EURO Italian (Italy,Euro) iw Hebrew iw_IL Hebrew (Israel) ja Japanese ja_JP Japanese (Japan) ko 韓文 ko_KR 韓文 (大韓民國) lt Lithuanian lt_LT Lithuanian (Lithuania) lv Latvian (Lettish) lv_LV Latvian (Lettish) (Latvia) mk Macedonian mk_MK Macedonian (Macedonia) nl Dutch nl_BE Dutch (Belgium) nl_BE_EURO Dutch (Belgium,Euro) nl_NL Dutch (Netherlands) nl_NL_EURO Dutch (Netherlands,Euro) no Norwegian no_NO Norwegian (Norway) no_NO_NY Norwegian (Norway,Nynorsk) pl Polish pl_PL Polish (Poland) pt Portuguese pt_BR Portuguese (Brazil) pt_PT Portuguese (Portugal) pt_PT_EURO Portuguese (Portugal,Euro) ro Romanian ro_RO Romanian (Romania) ru Russian ru_RU Russian (Russia) sh Serbo-Croatian sh_YU Serbo-Croatian (Yugoslavia) sk Slovak sk_SK Slovak (Slovakia) sl Slovenian sl_SI Slovenian (Slovenia) sq Albanian sq_AL Albanian (Albania) sr Serbian sr_YU Serbian (Yugoslavia) sv Swedish sv_SE Swedish (Sweden) th Thai th_TH Thai (Thailand) tr Turkish tr_TR Turkish (Turkey) uk Ukrainian uk_UA Ukrainian (Ukraine) zh 中文 zh_CN 中文 (中華人民共和國) zh_HK 中文 (香港) zh_TW 中文 (臺灣) ======System property======== -- listing properties -- java.runtime.name=Java(TM) 2 Runtime Environment, Stand... sun.boot.library.path=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0... java.vm.version=1.3.0_02 java.vm.vendor=Sun Microsystems Inc. java.vendor.url=http://java.sun.com/ path.separator=; java.vm.name=Java HotSpot(TM) Client VM file.encoding.pkg=sun.io java.vm.specification.name=Java Virtual Machine Specification user.dir=D:\java\src\char_test java.runtime.version=1.3.0_02 java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment os.arch=x86 java.io.tmpdir=D:\TEMP\ line.separator=
java.vm.specification.vendor=Sun Microsystems Inc. java.awt.fonts= os.name=Windows 98 java.library.path=C:\WINDOWS;.;C:\WINDOWS\SYSTEM;C:\WIN... java.specification.name=Java Platform API Specification java.class.version=47.0 os.version=4.90 user.home=C:\WINDOWS user.timezone=Asia/Shanghai java.awt.printerjob=sun.awt.windows.WPrinterJob file.encoding=GBK
java.specification.version=1.3
user.name=Sicci
java.class.path=d:\java\classes
java.vm.specification.version=1.0
java.home=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_02
user.language=zh
java.specification.vendor=Sun Microsystems Inc.
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.3.0_02
java.ext.dirs=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0...
sun.boot.class.path=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0...
java.vendor=Sun Microsystems Inc.
file.separator=\
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
user.region=CN
sun.cpu.isalist=pentium i486 i386
|
Hello, it's: Tue Jul 30 11:53:27 CST 2002 ======System available locales:======== en English en_US English (United States) ar Arabic ar_AE Arabic (United Arab Emirates) ar_BH Arabic (Bahrain) ar_DZ Arabic (Algeria) ar_EG Arabic (Egypt) ar_IQ Arabic (Iraq) ar_JO Arabic (Jordan) ar_KW Arabic (Kuwait) ar_LB Arabic (Lebanon) ar_LY Arabic (Libya) ar_MA Arabic (Morocco) ar_OM Arabic (Oman) ar_QA Arabic (Qatar) ar_SA Arabic (Saudi Arabia) ar_SD Arabic (Sudan) ar_SY Arabic (Syria) ar_TN Arabic (Tunisia) ar_YE Arabic (Yemen) be Byelorussian be_BY Byelorussian (Belarus) bg Bulgarian bg_BG Bulgarian (Bulgaria) ca Catalan ca_ES Catalan (Spain) ca_ES_EURO Catalan (Spain,Euro) cs Czech cs_CZ Czech (Czech Republic) da Danish da_DK Danish (Denmark) de German de_AT German (Austria) de_AT_EURO German (Austria,Euro) de_CH German (Switzerland) de_DE German (Germany) de_DE_EURO German (Germany,Euro) de_LU German (Luxembourg) de_LU_EURO German (Luxembourg,Euro) el Greek el_GR Greek (Greece) en_AU English (Australia) en_CA English (Canada) en_GB English (United Kingdom) en_IE English (Ireland) en_IE_EURO English (Ireland,Euro) en_NZ English (New Zealand) en_ZA English (South Africa) es Spanish es_AR Spanish (Argentina) es_BO Spanish (Bolivia) es_CL Spanish (Chile) es_CO Spanish (Colombia) es_CR Spanish (Costa Rica) es_DO Spanish (Dominican Republic) es_EC Spanish (Ecuador) es_ES Spanish (Spain) es_ES_EURO Spanish (Spain,Euro) es_GT Spanish (Guatemala) es_HN Spanish (Honduras) es_MX Spanish (Mexico) es_NI Spanish (Nicaragua) es_PA Spanish (Panama) es_PE Spanish (Peru) es_PR Spanish (Puerto Rico) es_PY Spanish (Paraguay) es_SV Spanish (El Salvador) es_UY Spanish (Uruguay) es_VE Spanish (Venezuela) et Estonian et_EE Estonian (Estonia) fi Finnish fi_FI Finnish (Finland) fi_FI_EURO Finnish (Finland,Euro) fr French fr_BE French (Belgium) fr_BE_EURO French (Belgium,Euro) fr_CA French (Canada) fr_CH French (Switzerland) fr_FR French (France) fr_FR_EURO French (France,Euro) fr_LU French (Luxembourg) fr_LU_EURO French (Luxembourg,Euro) hr Croatian hr_HR Croatian (Croatia) hu Hungarian hu_HU Hungarian (Hungary) is Icelandic is_IS Icelandic (Iceland) it Italian it_CH Italian (Switzerland) it_IT Italian (Italy) it_IT_EURO Italian (Italy,Euro) iw Hebrew iw_IL Hebrew (Israel) ja Japanese ja_JP Japanese (Japan) ko Korean ko_KR Korean (South Korea) lt Lithuanian lt_LT Lithuanian (Lithuania) lv Latvian (Lettish) lv_LV Latvian (Lettish) (Latvia) mk Macedonian mk_MK Macedonian (Macedonia) nl Dutch nl_BE Dutch (Belgium) nl_BE_EURO Dutch (Belgium,Euro) nl_NL Dutch (Netherlands) nl_NL_EURO Dutch (Netherlands,Euro) no Norwegian no_NO Norwegian (Norway) no_NO_NY Norwegian (Norway,Nynorsk) pl Polish pl_PL Polish (Poland) pt Portuguese pt_BR Portuguese (Brazil) pt_PT Portuguese (Portugal) pt_PT_EURO Portuguese (Portugal,Euro) ro Romanian ro_RO Romanian (Romania) ru Russian ru_RU Russian (Russia) sh Serbo-Croatian sh_YU Serbo-Croatian (Yugoslavia) sk Slovak sk_SK Slovak (Slovakia) sl Slovenian sl_SI Slovenian (Slovenia) sq Albanian sq_AL Albanian (Albania) sr Serbian sr_YU Serbian (Yugoslavia) sv Swedish sv_SE Swedish (Sweden) th Thai th_TH Thai (Thailand) tr Turkish tr_TR Turkish (Turkey) uk Ukrainian uk_UA Ukrainian (Ukraine) zh Chinese zh_CN Chinese (China) zh_HK Chinese (Hong Kong) zh_TW Chinese (Taiwan) ======System property======== -- listing properties -- java.runtime.name=Java(TM) 2 Runtime Environment, Stand... sun.boot.library.path=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0... java.vm.version=1.3.0_02 java.vm.vendor=Sun Microsystems Inc. java.vendor.url=http://java.sun.com/ path.separator=; java.vm.name=Java HotSpot(TM) Client VM file.encoding.pkg=sun.io java.vm.specification.name=Java Virtual Machine Specification user.dir=D:\java\src\char_test java.runtime.version=1.3.0_02 java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment os.arch=x86 java.io.tmpdir=D:\TEMP\ line.separator=
java.vm.specification.vendor=Sun Microsystems Inc. java.awt.fonts= os.name=Windows 98 java.library.path=C:\WINDOWS;.;C:\WINDOWS\SYSTEM;C:\WIN... java.specification.name=Java Platform API Specification java.class.version=47.0 os.version=4.90 user.home=C:\WINDOWS user.timezone=Asia/Shanghai java.awt.printerjob=sun.awt.windows.WPrinterJob file.encoding=Cp1252
java.specification.version=1.3
user.name=Sicci
java.class.path=d:\java\classes
java.vm.specification.version=1.0
java.home=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_02
user.language=en
java.specification.vendor=Sun Microsystems Inc.
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.3.0_02
java.ext.dirs=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0...
sun.boot.class.path=C:\PROGRAM FILES\JavaSOFT\JRE\1.3.0_0...
java.vendor=Sun Microsystems Inc.
file.separator=\
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
user.region=GB
sun.cpu.isalist=pentium i486 i386
|
結論1:
JVM的缺省編碼方式由系統的“本地語言環境”設置確定,和操作系統的類型無關
。所以當設置成相同的LOCALE時,Linux和Windows下的缺省編碼方式是沒有區別的(可以認為cp1252=ISO-8859-1都是一樣的西文編碼方式,只包含255以下的拉丁字符),因此后面的測試2我只列出了GNU/Linux下LOCALE分別設置成zh_CN和en_US的測試結果輸出。以下測試如果在Windows下分別按照不同的區域和字符集設置后試驗的輸出是一樣的。
通過這個
LANG=en_US LC_ALL=en_US
|
LANG=zh_CN LC_ALL=zh_CN.GBK
|
========testing1: write hello world to files======== [test 1-1]: with system default encoding=ISO-8859-1 string=Hello world 世界你好 length=20 char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN char[12]='? byte=-54 \uFFFFFFCA short=202 \uCA LATIN_1_SUPPLEMENT char[13]='? byte=-64 \uFFFFFFC0 short=192 \uC0 LATIN_1_SUPPLEMENT char[14]='? byte=-67 \uFFFFFFBD short=189 \uBD LATIN_1_SUPPLEMENT char[15]='? byte=-25 \uFFFFFFE7 short=231 \uE7 LATIN_1_SUPPLEMENT char[16]='? byte=-60 \uFFFFFFC4 short=196 \uC4 LATIN_1_SUPPLEMENT char[17]='? byte=-29 \uFFFFFFE3 short=227 \uE3 LATIN_1_SUPPLEMENT char[18]='? byte=-70 \uFFFFFFBA short=186 \uBA LATIN_1_SUPPLEMENT char[19]='? byte=-61 \uFFFFFFC3 short=195 \uC3 LATIN_1_SUPPLEMENT
第1步:在英文編碼環境下,雖然屏幕上正確的顯示了中文, 但實際上它打印的是“半個”漢字,將結果寫入第1個文件 hello.orig.html
[test 1-2]: getBytes with platform default encoding and decoding as gb2312:
string=Hello world ???? length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='?' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='?' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='?' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='?' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
按系統缺省編碼重新變成字節流,然后按照GB2312方式解碼,這里雖然打印出的是問號 (因為當前的英文環境下系統對于255以上的字符是不知道用什么字符表示的,因此全部用?顯示) 但從相應的UNICODE MAPPING和SHORT值我們可以知道字符是正確的中文
但下一步的寫入第2個文件html.gb2312.html, 沒有指定編碼方式(按系統缺省的ISO-8859-1編碼方式), 因此從后面的測試2-2讀取的結果是真的'?'了
[test 1-3]: convert string to UTF8
string=Hello world 涓栫晫浣犲ソ length=24
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='? byte=-28 \uFFFFFFE4 short=228 \uE4 LATIN_1_SUPPLEMENT
char[13]='? byte=-72 \uFFFFFFB8 short=184 \uB8 LATIN_1_SUPPLEMENT
char[14]='? byte=-106 \uFFFFFF96 short=150 \u96 LATIN_1_SUPPLEMENT
char[15]='? byte=-25 \uFFFFFFE7 short=231 \uE7 LATIN_1_SUPPLEMENT
char[16]='? byte=-107 \uFFFFFF95 short=149 \u95 LATIN_1_SUPPLEMENT
char[17]='? byte=-116 \uFFFFFF8C short=140 \u8C LATIN_1_SUPPLEMENT
char[18]='? byte=-28 \uFFFFFFE4 short=228 \uE4 LATIN_1_SUPPLEMENT
char[19]='? byte=-67 \uFFFFFFBD short=189 \uBD LATIN_1_SUPPLEMENT
char[20]='? byte=-96 \uFFFFFFA0 short=160 \uA0 LATIN_1_SUPPLEMENT
char[21]='? byte=-27 \uFFFFFFE5 short=229 \uE5 LATIN_1_SUPPLEMENT
char[22]='? byte=-91 \uFFFFFFA5 short=165 \uA5 LATIN_1_SUPPLEMENT
char[23]='? byte=-67 \uFFFFFFBD short=189 \uBD LATIN_1_SUPPLEMENT
第3個試驗,將字符流按照UTF8方式編碼后,寫入第3個測試文件hello.utf8.html, 我們可以看到UTF8對英文沒有影響,但對于其他文字使用了3字節編碼方式, 因此比GB2312編碼方式的存儲要大50%,
========Testing2: reading and decoding from files========
[test 2-1]: read hello.orig.html: decoding with system default encoding
string=Hello world 世界你好 length=20
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='? byte=-54 \uFFFFFFCA short=202 \uCA LATIN_1_SUPPLEMENT
char[13]='? byte=-64 \uFFFFFFC0 short=192 \uC0 LATIN_1_SUPPLEMENT
char[14]='? byte=-67 \uFFFFFFBD short=189 \uBD LATIN_1_SUPPLEMENT
char[15]='? byte=-25 \uFFFFFFE7 short=231 \uE7 LATIN_1_SUPPLEMENT
char[16]='? byte=-60 \uFFFFFFC4 short=196 \uC4 LATIN_1_SUPPLEMENT
char[17]='? byte=-29 \uFFFFFFE3 short=227 \uE3 LATIN_1_SUPPLEMENT
char[18]='? byte=-70 \uFFFFFFBA short=186 \uBA LATIN_1_SUPPLEMENT
char[19]='? byte=-61 \uFFFFFFC3 short=195 \uC3 LATIN_1_SUPPLEMENT
按系統從中間存儲hello.orig.html文件中讀取相應文件, 雖然是按字節方式(半個“字”)讀取的,但由于能完整的還原,因此輸出顯示沒有錯誤。 其實PHP等應用很少出現字符集問題其實就是這個原因,全程都是按字節流方式處理, 很好的還原了輸入,但這樣處理的同時也失去了對字符的控制
[test 2-2]: read hello.gb2312.html: decoding as GB2312
string=Hello world ???? length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='?' byte=63 \u3F short=63 \u3F BASIC_LATIN
char[13]='?' byte=63 \u3F short=63 \u3F BASIC_LATIN
char[14]='?' byte=63 \u3F short=63 \u3F BASIC_LATIN
char[15]='?' byte=63 \u3F short=63 \u3F BASIC_LATIN
最慘的就是輸出的時候這些'?'真的是問號char(63)了, 數據如果是這樣就真的沒救了
[test 2-3]: read hello.utf8.html: decoding as UTF8
string=Hello world ???? length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='?' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='?' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='?' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='?' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
great! 字符雖然顯示為'?',但實際上字符的解碼是正確的, 從相應的UNICODE MAPPING就可以看的出來。
|
========Testing1: write hello world to files======== [test 1-1]: with system default encoding=GBK string=Hello world 世界你好 length=16 char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN char[12]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS char[13]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS char[14]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS char[15]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
注意:在新的語言環境中做以上測試需要將源程序重新編譯, 最早的字節流到字符流的解碼過程從JavaC編譯源文件就開始了, 這個測試和剛才最大的不同在于源文件中的“世界你好”這4個字是否按中文編碼方式 編譯導程序里的,而不是按字節方式編譯成8個字符(實際上對應的是8個字節)在程序里。
[test 1-2]: getBytes with platform default encoding and decoding as gb2312:
string=Hello world 世界你好 length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
在中文環境下,解碼和上面缺省的編碼是一致的,因此輸出一致
[test 1-3]: convert string to UTF8
string=Hello world 涓栫晫浣犲ソ length=18
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='涓' byte=-109 \uFFFFFF93 short=28051 \u6D93 CJK_UNIFIED_IDEOGRAPHS
char[13]='栫' byte=43 \u2B short=26667 \u682B CJK_UNIFIED_IDEOGRAPHS
char[14]='晫' byte=107 \u6B short=26219 \u666B CJK_UNIFIED_IDEOGRAPHS
char[15]='浣' byte=99 \u63 short=28003 \u6D63 CJK_UNIFIED_IDEOGRAPHS
char[16]='犲' byte=-78 \uFFFFFFB2 short=29362 \u72B2 CJK_UNIFIED_IDEOGRAPHS
char[17]='ソ' byte=-67 \uFFFFFFBD short=12477 \u30BD KATAKANA
其實我們用于測試的終端窗口就是一個GBK字符集的應用, 這個輸出其實都是把UNICODE按GBK字符集解碼的效果。
========Testing2: reading and decoding from files========
[test 2-1]: read hello.orig.html: decoding with system default encoding
string=Hello world 世界你好 length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
[test 2-2]: read hello.gb2312.html: decoding as GB2312
string=Hello world 世界你好 length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
[test 2-3]: read hello.utf8.html: decoding as UTF8
string=Hello world 世界你好 length=16
char[0]='H' byte=72 \u48 short=72 \u48 BASIC_LATIN
char[1]='e' byte=101 \u65 short=101 \u65 BASIC_LATIN
char[2]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[3]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[4]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[5]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[6]='w' byte=119 \u77 short=119 \u77 BASIC_LATIN
char[7]='o' byte=111 \u6F short=111 \u6F BASIC_LATIN
char[8]='r' byte=114 \u72 short=114 \u72 BASIC_LATIN
char[9]='l' byte=108 \u6C short=108 \u6C BASIC_LATIN
char[10]='d' byte=100 \u64 short=100 \u64 BASIC_LATIN
char[11]=' ' byte=32 \u20 short=32 \u20 BASIC_LATIN
char[12]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[13]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[14]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[15]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
結論:如果后臺數據采用UNICODE方式的存儲 然后根據需要指定字符集編碼、解碼方式,則應用幾乎可以不受前端應用所處 環境字符集設置的影響
|
試驗2的一些結論:
- 所有的應用都是按照字節流=>字符流=>字節流方式進行的處理的:
byte_stream ==[input decoding]==> unicode_char_stream ==[output encoding]==> byte_stream;
- 在Java字節流到字符流(或者反之)都是含有隱含的解碼處理的(缺省是按照系統缺省編碼方式);
- 最早的字節流解碼過程從javac的代碼編譯就開始了;
- Java中的字符character存儲單位是雙字節的UNICODE;
首先一個概念:即使是基于Java的WEB應用,在服務器和客戶端之間傳遞的仍然是字節流,比如我從一個中文客戶端的瀏覽器表單中提交“世界你好”這4個中文字到服務器時:首先瀏覽器按照GBK方式編碼成字節流CA C0 BD E7 C4 E3 BA C3,然后8個字節按照URLEncoding的規范轉成:%CA%C0%BD%E7%C4%E3%BA%C3,服務器端的Servlet接收到請求后應該按什么解碼處理,輸出時又應該按什么方式編碼行字節流呢?
在目前的Servlet的規范中,如果不指定的話通過WEB提交時的輸入ServletRequest和輸出時的ServletResponse缺省都是ISO-8859-1方式編/碼解碼的(注意,這里的編碼/解碼方式是和操作系統環境中的語言環境是無關的)。因此,即使服務器操作系統的語言環境是中文,上面輸入的請求仍然按英文解碼成8個UNICODE字符,輸出時仍按照英文再編碼成8個字節,雖然這樣在瀏覽器端如果設置是中文能夠正確顯示,但實際上讀寫的是“字節”,正確的方式是應該根據客戶端瀏覽器設置ServletRequest和ServletResponse用相應語言的編碼方式進行輸入解碼/輸入編碼,HelloUnicodeServlet.java就是這樣一個監測客戶端瀏覽器語言設置的例子:
當根據瀏覽器的頭信息中的"Accept-Language"為zh-cn(中文)時,設置請求的解碼方式和輸出的字符集編碼方式使用GBK:
//auto detect broswer's languages
String clientLanguage = req.getHeader("Accept-Language");
//for Simplied Chinese
if ( clientLanguage.equals("zh-cn") ) {
req.setCharacterEncoding("GBK");
res.setContentType("text/html; charset=GBK");
}
輸出為:
'世界你好' length=4
ServletRequest's Charset Encoding = GBK
ServletResponse's Charset Encoding = GBK
char[0]='世' byte=22 \u16 short=19990 \u4E16 CJK_UNIFIED_IDEOGRAPHS
char[1]='界' byte=76 \u4C short=30028 \u754C CJK_UNIFIED_IDEOGRAPHS
char[2]='你' byte=96 \u60 short=20320 \u4F60 CJK_UNIFIED_IDEOGRAPHS
char[3]='好' byte=125 \u7D short=22909 \u597D CJK_UNIFIED_IDEOGRAPHS
再做一個試驗:把程序開頭部分的瀏覽器自動檢測功能注釋掉,再次的輸出結果就是和目前很多應用一樣其實是按ISO-8859-1方式解碼/編碼的“字節應用”了:
'世界你好' length=8
ServletRequest's Charset Encoding = null
ServletResponse's Charset Encoding = ISO-8859-1
char[0]='? byte=-54 \uFFFFFFCA short=202 \uCA LATIN_1_SUPPLEMENT
char[1]='? byte=-64 \uFFFFFFC0 short=192 \uC0 LATIN_1_SUPPLEMENT
char[2]='? byte=-67 \uFFFFFFBD short=189 \uBD LATIN_1_SUPPLEMENT
char[3]='? byte=-25 \uFFFFFFE7 short=231 \uE7 LATIN_1_SUPPLEMENT
char[4]='? byte=-60 \uFFFFFFC4 short=196 \uC4 LATIN_1_SUPPLEMENT
char[5]='? byte=-29 \uFFFFFFE3 short=227 \uE3 LATIN_1_SUPPLEMENT
char[6]='? byte=-70 \uFFFFFFBA short=186 \uBA LATIN_1_SUPPLEMENT
char[7]='? byte=-61 \uFFFFFFC3 short=195 \uC3 LATIN_1_SUPPLEMENT
雖然這樣的輸出結果如果在瀏覽器中設置用中文字符集也能正確顯示,但實際上處理的已經是“字節”而不是處理中文“字符”了。ServletRequest 和 ServletResponse 缺省使用ISO-8859-1字符集解碼/編碼的具體定義請參考:
http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletRequest.html#setCharacterEncoding(java.lang.String)
http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletResponse.html#setContentType()以前能夠配置讓一個WEB應用能夠在GBK方式編碼的中文Windows2000服務器上和按ISO-8859-1方式編碼的GNU/Linux上都能夠正確的顯示中文一直讓我迷惑了很久。我仔細想了一下,后來終于想明白了,在一個國際化的應用中:ServletRequest和ServletResponse的編碼/解碼方式的確不應該根據服務器設置成固定的字符集,而應該是面向客戶端語言環境進行輸入/輸出編碼方式的自適應。一個按照國際化規范設計的WEB應用中:
- 在Servlet的源代碼中盡量不要有中文:因為在MVC模式中,Servlet主要是控制器(C)的角色,因此,應該通過ResourceBundle機制由Servlet控制轉向到相應的顯示器(JSP或者XSLT)中,所以應該將與本地界面語言相關的界面顯示的部分從Servlet和后臺的模塊中完全剝離出來,放到相應的ResourceBundle文件中或者XSLT文件中。這樣源程序里完全是英文,編譯時完全不需要考慮字符集的問題。
如果Servlet實在需要包含中文,則需要設置應用服務器的Javac編譯選項,加上-encoding選項成系統缺省的字符集,如果把用中文編寫的字符按照英文方式解碼編譯,然后再按照英文方式輸出,雖然結果表面正確,實際上都成了面向“字節”編程。
- 在Servlet層,應該像GOOGLE搜索引擎那樣,設計成能夠根據客戶端瀏覽器的語言環境自適應輸出,為了判斷瀏覽器的語言Servlet中應該有類似以下的代碼:
public void doGet (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
//從HTTP請求的頭信息中獲取客戶端的語言設置
String clientLanguage = req.getHeader("Accept-Language");
//簡體中文瀏覽器
if ( clientLanguage.equals("zh-cn") ) {
req.setCharacterEncoding("GBK");
res.setContentType("text/html; charset=GBK");
}
//繁體中文瀏覽器
else if ( clientLanguage.equals("zh-tw") ) {
req.setCharacterEncoding("BIG5");
res.setContentType("text/html; charset=BIG5");
}
//日文瀏覽器
else if ( clientLanguage.equals("jp") ) {
req.setCharacterEncoding("SJIS");
res.setContentType("text/html; charset=SJIS");
}
//缺省認為是英文瀏覽器
else {
req.setCharacterEncoding("ISO-8859-1");
res.setContentType("text/html; charset=ISO-8859-1");
}
...
//設置好request的解碼方式和response的編碼方式后,進行后續的操作。
//比如再轉向到HelloWorld.gbk.jsp HelloWorld.big5.jsp HelloWorld.jis.jsp等
}
而SERVLET缺省將字符集設置為ISO-8859-1也許是標準制定者認為英文的瀏覽器占大多數吧(而且按照ISO-8859-1方式輸出界面往往也是正確的)。
結論:
過以上幾個Java試驗程序得出的一些結論:
- Java環境是基于操作系統上的一個虛擬機應用,因此,如果操作系統遵循國際化規范:JVM的缺省編碼方式可以通過修改操作系統的LOCALE設置實現。對于一個Java應用來說,只要將LINUX的缺省編碼方式設置成GBK,其文字編碼處理應該和中文Windows平臺上的表現是一致的。
???redhat 6.X使用linux內核的是基于glibc2.1.X,不支持中文LOCALE,因此無法通過改變LOCALE設置改變JVM的缺省編碼方式,linux內核2.4開始基于glibc.2.2.x,對中文LOCALE有了比較好的支持。 - 不同的JVM對字符集的支持程度不同:
??? 比如:IBM的JVM1.3.0開始支持GB18030,SUN的JVM從1.4開始支持GB18030 - 正確的編碼方式不一定表示能正確的顯示,正確的顯示還要需要相應的前端顯示系統(字庫)的支持
??? 但對于Linux上的服務應用來說,往往只要能確認字符正確的按照指定的方式編碼就夠了 - 如果應用的是基于UNICODE的編碼方式處理并使用UTF8字符集做集中存儲,這樣最便于根據客戶端語言環境做本地化輸出;
根據以上結論,設計一個適應多語言環境的應用,可以考慮一下2個應用處理模式:
隨著UNICODE被愈來愈多的系統和平臺支持:Python Perl Glibc等,但我們應該珍惜一開始就按照國際化規范設計Java,并將其和新發展起來的XML規范相配合,相信符合國際化規范的應用設計從長遠來看會展現出更多的優勢。
TODO:
數據庫應用中的字符集問題試驗:MySQL Oracle JDBC
參考文檔:
Java的國際化設計
http://java.sun.com/docs/books/tutorial/i18n/index.html
Linux 國際化本地化和中文化
http://www.linuxforum.net/doc/i18n-new.html
Linux 程序員必讀:中文化與GB18030標準
http://www.ccidnet.com/tech/os/2001/07/31/58_2811.html
Unicode FAQ
http://www.cl.cam.ac.uk/~mgk25/unicode.html
http://www.linuxforum.net/books/UTF-8-Unicode.html (中文版)
Java 編程技術中漢字問題的分析及解決
http://www-900.ibm.com/developerWorks/cn/java/java_chinese/index.shtml
漢字的編碼方式:
http://www.unihan.com.cn/cjk/ana17.htm
不同版本的JVM支持的編碼方式
http://java.sun.com/j2se/1.3/docs/guide/intl/encoding.doc.html
http://java.sun.com/j2se/1.4/docs/guide/intl/encoding.doc.html
附錄: