發信人: shaw (旺仔), 信區: JAVA
標 題: Java 編程技術中漢字問題的分析及解決(轉自IBM)
發信站: 碧海青天 (Sun Dec 24 21:12:02 2000), 轉信
Java 編程技術中漢字問題的分析及解決
段明輝
自由撰稿人
2000 年 11月 8日
----------------------------------------------------------------------------
----
在基于 Java [EMAIL PROTECTED]
[EMAIL PROTECTED] 語言
默認的編碼方式是UNICODE ,而我們中國人通常使用的文件和數據庫都是基于 GB2312
或者 BIG5 等方式編碼的,怎樣才能夠恰當地選擇漢字編碼方式並正確地處理漢字的編
碼呢?本文將從漢字編碼的常識入手,結合 Java 編程實例,分析以上兩個問題並提出
解決它們的方案。
----------------------------------------------------------------------------
----
現在 Java [EMAIL PROTECTED] Sun 公司開發 Java 語言的時候
,就已經考慮到對非英文字符的支持了。Sun 公司公布的 Java 運行環境(JRE)本身就
分英文版和國際版,但只有國際版才支持非英文字符。不過在 Java 編程語言的應用中
,對中文字符的支持並非如同 Java Soft 的標準規范中所宣稱的那樣完美,因為中文字
[EMAIL PROTECTED]@系統對中文字符的支持也不盡相同,所以會有許多和漢
字編碼處理有關的問題在我們進行應用開發中困擾著我們。有很多關于這些問題的解答
[EMAIL PROTECTED] Java 中文問題的系統
研究並不多,本文從漢字編碼常識出發,分析 Java 中文問題,希望對大家解決這個問
題有所幫助。
漢字編碼的常識
[EMAIL PROTECTED]@個字節來表示的,最常用的編碼方法是 ASCII [EMAIL PROTECTED]
字節最多只能區分256個字符,而漢字成千上萬,所以現在都以雙字節來表示漢字,為了
[EMAIL PROTECTED]
。我們經常碰到的編碼方式有 GB2312、BIG5、UNICODE 等。關于具體編碼方式的詳細資
[EMAIL PROTECTED] GB2312 和 UNI
CODE。GB2312 [EMAIL PROTECTED]@個由中華人民共
和國國家標準總局發布的關于簡化漢字的編碼,通行于中國大陸地區及新加坡,簡稱國
[EMAIL PROTECTED]
[EMAIL PROTECTED] 碼是微
軟提出的解決多國字符問題的多字節等長編碼,它對英文字符採取前面加“0”字節的策
略實現等長兼容。如 “A” 的 ASCII 碼為0x41,UNICODE 就為0x00,0x41。利用特殊
的工具各種編碼之間可以互相轉換。
Java 中文問題的初步認識
我們基于 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
[EMAIL PROTECTED] UNICODE ,而是國標碼等。從 Java 源代碼
到得到正確的結果,要經過 “Java 源代碼-> Java 字節碼-> ;虛擬機->[EMAIL
PROTECTED]>顯
[EMAIL PROTECTED]
夠使最終的顯示結果正確。
“ Java 源代碼-> Java 字節碼”,標準的 Java 編譯器 javac 使用的字符集是系統默
認的字符集,比如在中文 Windows [EMAIL PROTECTED] GBK ,而在 Linux [EMAIL
PROTECTED]
ISO-8859-1,所以大家會發現在 Linux [EMAIL PROTECTED]
了問題,解決的辦法就是在編譯的時候添加 encoding 參數,這樣才能夠與平台無關。
用法是
javac □encoding GBK。
“ Java 字節碼->虛擬機->[EMAIL PROTECTED] Java 運行環境 (JRE)
分英文版和國際版,
但只有國際版才支持非英文字符。 Java 開發工具包 (JDK) 肯定支持多國字符,但並
非所有的計算機用戶都安裝了 JDK [EMAIL PROTECTED] Ja
va ,都內嵌了 JRE 的國際版本,為自己支持多國字符提供了方便。
[EMAIL PROTECTED]>[EMAIL PROTECTED]@
系統如果不搭配特殊的應用軟件的話,是肯定不能夠顯示中文的。
[EMAIL PROTECTED] Java 編程過程中,對中文字符進行正確的編碼轉換。例如,向
網頁輸出中文字符串的時候,不論你是用
out.println(string); // string 是含中文的字符串
還是用
<%=string%>,都必須作 UNICODE 到 GBK 的轉換,或者手動,或者自動。在 JSP 1.0中
,可以定義輸出字符集,從而實現內碼的自動轉換。用法是
<[EMAIL PROTECTED] ContentType=”text/html;charset=gb2312” %>
[EMAIL PROTECTED] 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 [EMAIL PROTECTED]
s2 。
Java 中文問題的表層分析及處理
背景
開發環境
JDK1.15
Vcafe2.0
JPadPro
服務器端
NT IIS
Sybase System
Jconnect(JDBC)
客戶端
IE5.0
Pwin98
.CLASS 文件存放在服務器端,由客戶端的瀏覽器運行 APPLET , APPLET 只起調入 FR
AME [EMAIL PROTECTED] Textfield ,TextArea,List,Choice 等。
I. 取中文
用 JDBC 執行 SELECT 語句從服務器端讀取數據(中文)後,將數據用 APPEND 方法加
到 TextArea(TA) ,不能正確顯示。但加到 List 中時,大部分漢字卻可正確顯示。
將數據按“ISO-8859-1” 編碼方式轉化為字節數組,再按系統缺省編碼方式 (Defaul
t Character Encoding) 轉化為 STRING ,即可在 TA 和 List 中正確顯示。
程序段如下:
dbstr2 = results.getString(1);
//After reading the result from DB server,converting it to string.
dbbyte1 = dbstr2.getBytes(“iso-8859-1”);
dbstr1 = new String(dbbyte1);
在轉換字符串時不採用系統默認編碼方式,而直接採用“ GBK” 或者 “GB2312” ,在
A 和 B 兩種情況下,從數據庫取數據都沒有問題。
II. 寫中文到數據庫
處理方式與“取中文”相逆,先將 SQL 語句按系統缺省編碼方式轉化為字節數組,再按
“ISO-8859-1”編碼方式轉化為 STRING ,最後送去執行,則中文信息可正確寫入數據
庫。
程序段如下:
sqlstmt = tf_input.getText();
//Before sending statement to DB server,converting it to sql statement.
dbbyte1 = sqlstmt.getBytes();
sqlstmt = newString(dbbyte1,”iso-8859-1”);
_stmt = _con.createStatement();
_stmt.executeUpdate(sqlstmt);
……
問題:如果客戶機上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 時(稱為 A 情況),
上述程序代碼可正確執行。但是如果客戶機只有瀏覽器,而沒有 JDK 和 CLASSPATH 時
(稱為 B 情況),則漢字無法正確轉換。
我們的分析:
1.經過測試,在 A 情況下,程序運行時系統的缺省編碼方式為 GBK 或者 GB2312 。在
B 情況下,程序啟動時瀏覽器的 JAVA 控制台中出現如下錯誤信息:
Can't find resource for sun.awt.windows.awtLocalization_zh_CN
然後系統的缺省編碼方式為“8859-1”。
2.如果在轉換字符串時不採用系統缺省編碼方式,而是直接採用 “GBK” 或“GB2312”
,則在 A 情況下程序仍然可正常運行,在 B 情況下,系統出現錯誤:
UnsupportedEncodingException。
3.在客戶機上,把 JDK 的 CLASSES.ZIP [EMAIL PROTECTED] CLASSPATH 只包
[EMAIL PROTECTED] .CLASS [EMAIL PROTECTED]
[EMAIL PROTECTED] CLASS [EMAIL PROTECTED]
sun.io.CharToByteDoubleByte.class。
[EMAIL PROTECTED] IMPORT 它,在 B 情況下
程序仍然無法正常運行。
4.在 A 情況下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,則
程序運行時測得默認編碼方式為“8859-1”,否則為 “GBK” 或 “GB2312” 。
如果 JDK 的版本為1.2以上的話,在 B 情況下遇到的問題得到了很好的解決,測試的步
[EMAIL PROTECTED]
[/b]Java 中文問題的根源分析及解決[/b]
在簡體中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Ja
va [EMAIL PROTECTED] PoorChinese 可以幫助我們得到這些屬性。
類 PoorChinese 的源代碼:
public class PoorChinese {
public static void main(String[] args) {
System.getProperties().list(System.out);
}
}
執行 java PoorChinese 後,我們會得到:
系統變量 file.encoding 的值為 GBK ,user.language 的值為 zh , user.region 的
值為 CN ,這些系統變量的值決定了系統默認的編碼方式是 GBK 。
在上述系統中,下面的代碼將 GB2312 文件轉換成 Big5 文件,它們能夠幫助我們理解
Java 中漢字編碼的轉化:
import java.io.*;
import java.util.*;
public class gb2big5 {
static int iCharNum=0;
public static void main(String[] args) {
System.out.println("Input GB2312 file, output Big5 file.");
if (args.length!=2) {
System.err.println("Usage: jview gb2big5 gbfile big5file");
System.exit(1);
}
String inputString = readInput(args[0]);
writeOutput(inputString,args[1]);
System.out.println("Number of Characters in file: "+iCharNum+".");
}
static void writeOutput(String str, String strOutFile) {
try {
FileOutputStream fos = new FileOutputStream(strOutFile);
Writer out = new OutputStreamWriter(fos, "Big5");
out.write(str);
out.close();
}
catch (IOException e) {
e.printStackTrace();
e.printStackTrace();
}
}
static String readInput(String strInFile) {
StringBuffer buffer = new StringBuffer();
try {
FileInputStream fis = new FileInputStream(strInFile);
InputStreamReader isr = new InputStreamReader(fis, "GB2312");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in.read()) > -1) {
iCharNum += 1;
buffer.append((char)ch);
}
in.close();
return buffer.toString();
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
編碼轉化的過程如下:
ByteToCharGB2312 CharToByteBig5
GB2312------------------>Unicode------------->Big5
執行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的內容是“今天星期三”,則得
到的文件 big5.txt 中的字符能夠正確顯示;而如果 gb.txt 的內容是“情人節快樂”
,則得到的文件 big5.txt 中對應于“節”和“樂”的字符都是符號“?”(0x3F),
可見 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 這兩個基本類並沒有編好
。
[EMAIL PROTECTED] Java [EMAIL PROTECTED]
的,所以在這些基本類發布之前,沒有經過嚴格的測試,所以對中文字符的支持並不像
Java Soft [EMAIL PROTECTED]
到了 Java Servlet [EMAIL PROTECTED] Java Servlet 的中文問題
[EMAIL PROTECTED]
[EMAIL PROTECTED]
[EMAIL PROTECTED] Servlet 解碼的源
代碼進行分析,因為他懷疑問題就出在解碼這部分。經過四個小時的奮鬥,他終于找到
了問題的根源所在。原來他的懷疑是正確的, Servlet 的解碼部分完全沒有考慮雙字節
,直接把 %XX [EMAIL PROTECTED]@個字符。(原來 Java Soft 也會犯這□低級的錯誤!)
如果你對這個問題有興趣或者遇到了同樣的煩惱的話,你可以按照他的步驟對 Servlet
.jar 進行修改:
找到源代碼 HttpUtils 中的 static private String parseName ,在返回前將 sb(S
tringBuffer) 復制成 byte bs[] ,然後 return new String(bs,”GB2312”)[EMAIL
PROTECTED]
述修改後就需要自己解碼了:
HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者
form=HttpUtils.parsePostData(……)
千萬別忘了編譯後放到 Servlet.jar 裡面。
五、 關于 Java 中文問題的總結
Java [EMAIL PROTECTED] Java 對多國字符有很好的支持。 Java 編程
[EMAIL PROTECTED] J
ava 的締造者 (Java Soft) 已經考慮到 Java 編程語言對多國字符的支持,只是現在
[EMAIL PROTECTED]@界標準化組織也
[EMAIL PROTECTED]@[EMAIL PROTECTED] ISO10646 ,它用四個
[EMAIL PROTECTED] Java Soft 能夠嚴格
地測試它的產品,為用戶帶來更多的方便。
[EMAIL PROTECTED]
是問題已經解決了的字符串。
String parseChinese(String in)
{
String s = null;
byte temp [];
if (in == null)
{
System.out.println("Warn:Chinese null founded!");
return new String("");
}
try
{
temp=in.getBytes("iso-8859-1");
temp=in.getBytes("iso-8859-1");
s = new String(temp);
}
{
System.out.println("Warn:Chinese null founded!");
return new String("");
}
try
{
temp=in.getBytes("iso-8859-1");
s = new String(temp);
}
catch(UnsupportedEncodingException e)
{
System.out.println (e.toString());
}
return s;
}
----------------------------------------------------------------------------
----
[EMAIL PROTECTED]
段明輝,清華大學電子工程系學生
現在正在清華大學微電子學研究所從事 Java 智能卡微處理器的研究和開發
領導 BBS 水木清華站的 Java 討論組,為眾多 Java 技術應用者提供解決方案
--
, ,
'( )\
| \ ___/ /
` \-' _ -\
/\ / / \/\)
/ ' | \ \
[m[1;35m※ 來源:.碧海青天 bbs.dlut.edu.cn.[FROM: 202.118.68.59][m
--
[m[1;32m※ 轉寄:.碧海青天 bbs.dlut.edu.cn.[FROM: palm][m
--
| This message was re-posted from [email protected]
| and converted from gb2312 to big5 by an automatic gateway.