天天看點

Java 程式設計技術中漢字問題的分析及解決

簡介: 在基于 java 語言的程式設計中,我們經常碰到漢字的處理及顯示的問題。一大堆看不懂的亂碼肯定不是我們願意看到的顯示效果,怎樣才能夠讓那些漢字正确顯示呢?java語言預設的編碼方式是unicode,而我們中國人通常使用的檔案和資料庫都是基于gb2312或者big5等方式編碼的,怎樣才能夠恰當地選擇漢字編碼方式并正确地處理漢字的編碼呢?本文将從漢字編碼的常識入手,結合java程式設計執行個體,分析以上兩個問題并提出解決它們的方案。

現在 java 程式設計語言已經廣泛應用于網際網路世界,早在 sun 公司開發 java 語言的時候,就已經考慮到對非英文字元的支援了。sun 公司公布的 java 運作環境(jre)本身就分英文版和國際版,但隻有國際版才支援非英文字元。不過在 java 程式設計語言的應用中,對中文字元的支援并非如同 java soft 的标準規範中所宣稱的那樣完美,因為中文字元集不隻一個,而且不同的作業系統對中文字元的支援也不盡相同,是以會有許多和漢字編碼處理有關的問題在我們進行應用開發中困擾着我們。有很多關于這些問題的解答,但都比較瑣碎,并不能夠滿足大家迫切解決問題的願望,關于 java 中文問題的系統研究并不多,本文從漢字編碼常識出發,分析 java 中文問題,希望對大家解決這個問題有所幫助。

<a>漢字編碼的常識</a>

我們知道,英文字元一般是以一個位元組來表示的,最常用的編碼方法是 ascii 。但一個位元組最多隻能區分256個字元,而漢字成千上萬,是以現在都以雙位元組來表示漢字,為了能夠與英文字元分開,每個位元組的最高位一定為1,這樣雙位元組最多可以表示64k格字元。我們經常碰到的編碼方式有 gb2312、big5、unicode 等。關于具體編碼方式的詳細資料,有興趣的讀者可以查閱相關資料。我膚淺談一下和我們關系密切的 gb2312 和 unicode。gb2312 碼,中華人民共和國國家标準漢字資訊交換用編碼,是一個由中華人民共和國國家标準總局釋出的關于簡化漢字的編碼,通行于中國大陸地區及新加坡,簡稱國标碼。兩個位元組中,第一個位元組(高位元組)的值為區号值加32(20h),第二個位元組(低位元組)的值為位号值加32(20h),用這兩個值來表示一個漢字的編碼。unicode 碼是微軟提出的解決多國字元問題的多位元組等長編碼,它對英文字元采取前面加“0”位元組的政策實作等長相容。如 “a” 的 ascii 碼為0x41,unicode 就為0x00,0x41。利用特殊的工具各種編碼之間可以互相轉換。

<a href="http://www.ibm.com/developerworks/cn/java/java_chinese/index.html#ibm-pcon">回頁首</a>

<a>java 中文問題的初步認識</a>

我們基于 java 程式設計語言進行應用開發時,不可避免地要進行中文。java 程式設計語言預設的編碼方式是 unicode,而我們通常使用的資料庫及檔案都是基于 gb2312 編碼的,我們經常碰到這樣的情況:浏覽基于 jsp 技術的網站看到的是亂碼,檔案打開後看到的也是亂碼,被 java 修改過的資料庫的内容在别的場合應用時無法繼續正确地提供資訊。

string senglish = “apple”;

string schinese = “蘋果”;

string s = “蘋果 apple ”;

senglish 的長度是5,schinese的長度是4,而 s 預設的長度是14。對于 senglish來說, java 中的各個類都支援得非常好,肯定能夠正确顯示。但對于 schinese 和 s 來說,雖然 java soft 聲明 java 的基本類已經考慮到對多國字元的支援(預設 unicode 編碼),但是如果作業系統的預設編碼不是 unicode ,而是國标碼等。從 java 源代碼到得到正确的結果,要經過 “java 源代碼-&gt; java 位元組碼-&gt; ;虛拟機-&gt;作業系統-&gt;顯示裝置”的過程。在上述過程中的每一步驟,我們都必須正确地處理漢字的編碼,才能夠使最終的顯示結果正确。

“ java 源代碼-&gt; java 位元組碼”,标準的 java 編譯器 javac 使用的字元集是系統預設的字元集,比如在中文 windows 作業系統上就是 gbk ,而在 linux 作業系統上就是iso-8859-1,是以大家會發現在 linux 作業系統上編譯的類中源檔案中的中文字元都出了問題,解決的辦法就是在編譯的時候添加 encoding 參數,這樣才能夠與平台無關。用法是

javac ?cencoding gbk。

“ java 位元組碼-&gt;虛拟機-&gt;作業系統”, java 運作環境 (jre) 分英文版和國際版,但隻有國際版才支援非英文字元。 java 開發工具包 (jdk) 肯定支援多國字元,但并非所有的計算機使用者都安裝了 jdk 。很多作業系統及應用軟體為了能夠更好的支援 java ,都内嵌了 jre 的國際版本,為自己支援多國字元提供了友善。

“作業系統-&gt;顯示裝置”,對于漢字來說,作業系統必須支援并能夠顯示它。英文作業系統如果不搭配特殊的應用軟體的話,是肯定不能夠顯示中文的。

還有一個問題,就是在 java 程式設計過程中,對中文字元進行正确的編碼轉換。例如,向網頁輸出中文字元串的時候,不論你是用

out.println(string);還是用

&lt;%=string%&gt;,都必須作 unicode 到 gbk 的轉換,或者手動,或者自動。在 jsp 1.0中,可以定義輸出字元集,進而實作内碼的自動轉換。用法是

&lt;%@page contenttype=”text/html;charset=gb2312” %&gt;

但是在一些 jsp 版本中并沒有提供對輸出字元集的支援,(例如 jsp 0.92),這就需要手動編碼輸出了,方法非常多。最常用的方法是

string s1 = request.getparameter(“keyword”);

string s2 = new string(s1.getbytes(“iso-8859-1”),”gbk”);

getbytes 方法用于将中文字元以“iso-8859-1”編碼方式轉化成位元組數組,而“gbk” 是目标編碼方式。我們從以iso-8859-1方式編碼的資料庫中讀出中文字元串 s1 ,經過上述轉換過程,在支援 gbk 字元集的作業系統和應用軟體中就能夠正确顯示中文字元串 s2 。

<a>java 中文問題的表層分析及處理</a>

背景

開發環境

jdk1.15

vcafe2.0

jpadpro

伺服器端

nt iis

sybase system

jconnect(jdbc)

用戶端

ie5.0

pwin98

?span &gt;

.class 檔案存放在伺服器端,由用戶端的浏覽器運作 applet , applet 隻起調入 frame 類等主程式的作用。界面包括 textfield ,textarea,list,choice 等。

i.用 jdbc 執行 select 語句從伺服器端讀取資料(中文)後,将資料用 append 方法加到 textarea(ta) ,不能正确顯示。但加到 list 中時,大部分漢字卻可正确顯示。

将資料按“iso-8859-1” 編碼方式轉化為位元組數組,再按系統預設編碼方式 (default character encoding) 轉化為 string ,即可在 ta 和 list 中正确顯示。

程式段如下:

在轉換字元串時不采用系統預設編碼方式,而直接采用“ gbk” 或者 “gb2312” ,在 a 和 b 兩種情況下,從資料庫取資料都沒有問題。

ii.處理方式與“取中文”相逆,先将 sql 語句按系統預設編碼方式轉化為位元組數組,再按“iso-8859-1”編碼方式轉化為 string ,最後送去執行,則中文資訊可正确寫入資料庫。

問題:如果客戶機上存在 classpath 指向 jdk 的 classes.zip 時(稱為 a 情況),上述程式代碼可正确執行。但是如果客戶機隻有浏覽器,而沒有 jdk 和 classpath 時(稱為 b 情況),則漢字無法正确轉換。

我們的分析:

1.經過測試,在 a 情況下,程式運作時系統的預設編碼方式為 gbk 或者 gb2312 。在 b 情況下,程式啟動時浏覽器的 java 控制台中出現如下錯誤資訊:

然後系統的預設編碼方式為“8859-1”。

2.如果在轉換字元串時不采用系統預設編碼方式,而是直接采用 “gbk” 或“gb2312”,則在 a 情況下程式仍然可正常運作,在 b 情況下,系統出現錯誤:

3.在客戶機上,把 jdk 的 classes.zip 解壓後,放在另一個目錄中, classpath 隻包含該目錄。然後一邊逐漸删除該目錄中的 .class 檔案,另一邊運作測試程式,最後發現在一千多個 class 檔案中,隻有一個是必不可少的,該檔案是:

将該檔案拷到伺服器端和其它的類放在一起,并在程式的開頭 import 它,在 b 情況下程式仍然無法正常運作。

4.在 a 情況下,如果在 classpth 中去掉 sun.io.chartobytedoublebyte.class ,則程式運作時測得預設編碼方式為“8859-1”,否則為 “gbk” 或 “gb2312” 。

如果 jdk 的版本為1.2以上的話,在 b 情況下遇到的問題得到了很好的解決,測試的步驟同上,有興趣的讀者可以嘗試一下。

<a>java 中文問題的根源分析及解決</a>

在簡體中文 ms windows 98 + jdk 1.3 下,可以用 system.getproperties() 得到 java 運作環境的一些基本屬性,類 poorchinese 可以幫助我們得到這些屬性。

類 poorchinese 的源代碼:

執行 java poorchinese 後,我們會得到:

系統變量 file.encoding 的值為 gbk ,user.language 的值為 zh , user.region 的值為 cn ,這些系統變量的值決定了系統預設的編碼方式是 gbk 。

在上述系統中,下面的代碼将 gb2312 檔案轉換成 big5 檔案,它們能夠幫助我們了解 java 中漢字編碼的轉化:

編碼轉化的過程如下:

gb2312------------------&gt;unicode-------------&gt;big5

執行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的内容是“今天星期三”,則得到的檔案 big5.txt 中的字元能夠正确顯示;而如果 gb.txt 的内容是“情人節快樂”,則得到的檔案 big5.txt 中對應于“節”和“樂”的字元都是符号“?”(0x3f),可見 sun.io.bytetochargb2312 和 sun.io.chartobytebig5 這兩個基本類并沒有編好。

正如上例一樣, java 的基本類也可能存在問題。由于國際化的工作并不是在國内完成的,是以在這些基本類釋出之前,沒有經過嚴格的測試,是以對中文字元的支援并不像 java soft 所聲稱的那樣完美。前不久,我的一位技術上的朋友發信給我說,他終于找到了java servlet 中文問題的根源。兩周以來,他一直為 java servlet 的中文問題所困擾,因為每面對一個含有中文字元的字元串都必須進行強制轉換才能夠得到正确的結果(這好象是大家公認的唯一的解決辦法)。後來,他确實不想如此繼續安分下去了,因為這樣的事情确實不應該是進階程式員所要做的工作,他就找出 servlet 解碼的源代碼進行分析,因為他懷疑問題就出在解碼這部分。經過四個小時的奮鬥,他終于找到了問題的根源所在。原來他的懷疑是正确的, servlet 的解碼部分完全沒有考慮雙位元組,直接把 %xx 當作一個字元。(原來 java soft 也會犯這幺低級的錯誤!)

如果你對這個問題有興趣或者遇到了同樣的煩惱的話,你可以按照他的步驟 對servlet.jar 進行修改:

找到源代碼 httputils 中的 static private string parsename ,在傳回前将 sb(stringbuffer) 複制成 byte bs[] ,然後 return new string(bs,”gb2312”)。作上述修改後就需要自己解碼了:

hashtable form=httputils .parsequerystring(request.getquerystring())或者

form=httputils.parsepostdata(……)

千萬别忘了編譯後放到 servlet.jar 裡面。

<a>關于 java 中文問題的總結</a>

java 程式設計語言成長于網絡世界,這就要求 java 對多國字元有很好的支援。 java 程式設計語言适應了計算的網絡化的需求,為它能夠在網絡世界迅速成長奠定了堅實的基礎。 java 的締造者 (java soft) 已經考慮到 java 程式設計語言對多國字元的支援,隻是現在的解決方案有很多缺陷在裡面,需要我們付諸一些補償性的措施。而世界标準化組織也在努力把人類所有的文字統一在一種編碼之中,其中一種方案是 iso10646 ,它用四個位元組來表示一個字元。當然,在這種方案未被采用之前,還是希望 java soft 能夠嚴格地測試它的産品,為使用者帶來更多的友善。

附一個用于從資料庫和網絡中取出 中文亂碼的處理函數,入參是有問題的字元串,出參是問題已經解決了的字元串。