在基於 Java 語言的編程中我們經常碰到漢字的處理及顯示的問題一大堆看不懂的亂碼肯定不是我們願意看到的顯示效果怎樣才能夠讓那些漢字正確顯示呢?Java語言默認的編碼方式是UNICODE而我們中國人通常使用的文件和數據庫都是基於GB或者BIG等方式編碼的怎樣才能夠恰當地選擇漢字編碼方式並正確地處理漢字的編碼呢?本文將從漢字編碼的常識入手結合Java編程實例分析以上兩個問題並提出解決它們的方案
現在 Java 編程語言已經廣泛應用於互聯網世界早在 Sun 公司開發 Java 語言的時候就已經考慮到對非英文字符的支持了Sun 公司公布的 Java 運行環境(JRE)本身就分英文版和國際版但只有國際版才支持非英文字符不過在 Java 編程語言的應用中對中文字符的支持並非如同 Java Soft 的標准規范中所宣稱的那樣完美因為中文字符集不只一個而且不同的操作系統對中文字符的支持也不盡相同所以會有許多和漢字編碼處理有關的問題在我們進行應用開發中困擾著我們有很多關於這些問題的解答但都比較瑣碎並不能夠滿足大家迫切解決問題的願望關於 Java 中文問題的系統研究並不多本文從漢字編碼常識出發分析 Java 中文問題希望對大家解決這個問題有所幫助
漢字編碼的常識
我們知道英文字符一般是以一個字節來表示的最常用的編碼方法是 ASCII 但一個字節最多只能區分個字符而漢字成千上萬所以現在都以雙字節來表示漢字為了能夠與英文字符分開每個字節的最高位一定為這樣雙字節最多可以表示K格字符我們經常碰到的編碼方式有 GBBIGUNICODE 等關於具體編碼方式的詳細資料有興趣的讀者可以查閱相關資料我膚淺談一下和我們關系密切的 GB 和 UNICODEGB 碼中華人民共和國國家標准漢字信息交換用編碼是一個由中華人民共和國國家標准總局發布的關於簡化漢字的編碼通行於中國大陸地區及新加坡簡稱國標碼兩個字節中第一個字節(高字節)的值為區號值加(H)第二個字節(低字節)的值為位號值加(H)用這兩個值來表示一個漢字的編碼UNICODE 碼是微軟提出的解決多國字符問題的多字節等長編碼它對英文字符采取前面加“”字節的策略實現等長兼容如 “A” 的 ASCII 碼為xUNICODE 就為xx利用特殊的工具各種編碼之間可以互相轉換
Java 中文問題的初步認識
我們基於 Java 編程語言進行應用開發時不可避免地要處理中文Java 編程語言默認的編碼方式是 UNICODE而我們通常使用的數據庫及文件都是基於 GB 編碼的我們經常碰到這樣的情況浏覽基於 JSP 技術的網站看到的是亂碼文件打開後看到的也是亂碼被 Java 修改過的數據庫的內容在別的場合應用時無法繼續正確地提供信息
String sEnglish = “apple”;
String sChinese = “蘋果”;
String s = “蘋果 apple ”;
sEnglish 的長度是sChinese的長度是而 s 默認的長度是對於 sEnglish來說 Java 中的各個類都支持得非常好肯定能夠正確顯示但對於 sChinese 和 s 來說雖然 Java Soft 聲明 Java 的基本類已經考慮到對多國字符的支持(默認 UNICODE 編碼)但是如果操作系統的默認編碼不是 UNICODE 而是國標碼等從 Java 源代碼到得到正確的結果要經過 “Java 源代碼> Java 字節碼> ;虛擬機>操作系統>顯示設備”的過程在上述過程中的每一步驟我們都必須正確地處理漢字的編碼才能夠使最終的顯示結果正確
“ Java 源代碼> Java 字節碼”標准的 Java 編譯器 javac 使用的字符集是系統默認的字符集比如在中文 Windows 操作系統上就是 GBK 而在 Linux 操作系統上就是ISO所以大家會發現在 Linux 操作系統上編譯的類中源文件中的中文字符都出了問題解決的辦法就是在編譯的時候添加 encoding 參數這樣才能夠與平台無關用法是
javac ?Cencoding GBK
“ Java 字節碼>虛擬機>操作系統” Java 運行環境 (JRE) 分英文版和國際版但只有國際版才支持非英文字符 Java 開發工具包 (JDK) 肯定支持多國字符但並非所有的計算機用戶都安裝了 JDK 很多操作系統及應用軟件為了能夠更好的支持 Java 都內嵌了 JRE 的國際版本為自己支持多國字符提供了方便
“操作系統>顯示設備”對於漢字來說操作系統必須支持並能夠顯示它英文操作系統如果不搭配特殊的應用軟件的話是肯定不能夠顯示中文的
還有一個問題就是在 Java 編程過程中對中文字符進行正確的編碼轉換例如向網頁輸出中文字符串的時候不論你是用
out
println(string);
還是用<%=string%>都必須作 UNICODE 到 GBK 的轉換或者手動或者自動在 JSP 中可以定義輸出字符集從而實現內碼的自動轉換用法是
<%@page contentType=”text/html;charset=gb
” %>
但是在一些 JSP 版本中並沒有提供對輸出字符集的支持(例如 JSP )這就需要手動編碼輸出了方法非常多最常用的方法是
String s
= request
getParameter(“keyword”);
String s
= new String(s
getBytes(“ISO
”)
”GBK”);
getBytes 方法用於將中文字符以“ISO”編碼方式轉化成字節數組而“GBK” 是目標編碼方式我們從以ISO方式編碼的數據庫中讀出中文字符串 s 經過上述轉換過程在支持 GBK 字符集的操作系統和應用軟件中就能夠正確顯示中文字符串 s
Java 中文問題的表層分析及處理
背景
開發環境
JDK
Vcafe
JPadPro
服務器端
NT IIS
Sybase System
Jconnect(JDBC)
客戶端
IE
Pwin
CLASS 文件存放在服務器端由客戶端的浏覽器運行 APPLET APPLET 只起調入 FRAME 類等主程序的作用界面包括 Textfield TextAreaListChoice 等
I用 JDBC 執行 SELECT 語句從服務器端讀取數據(中文)後將數據用 APPEND 方法加到 TextArea(TA) 不能正確顯示但加到 List 中時大部分漢字卻可正確顯示
將數據按“ISO” 編碼方式轉化為字節數組再按系統缺省編碼方式 (Default Character Encoding) 轉化為 STRING 即可在 TA 和 List 中正確顯示
程序段如下
dbstr
= results
getString(
);
//After reading the result from DB server
converting it to string
dbbyte
= dbstr
getBytes(“iso
”);
dbstr
= new String(dbbyte
);
在轉換字符串時不采用系統默認編碼方式而直接采用“ GBK” 或者 “GB” 在 A 和 B 兩種情況下從數據庫取數據都沒有問題
II處理方式與“取中文”相逆先將 SQL 語句按系統缺省編碼方式轉化為字節數組再按“ISO”編碼方式轉化為 STRING 最後送去執行則中文信息可正確寫入數據庫
程序段如下
sqlstmt = tf_input
getText();
//Before sending statement to DB server
converting it to sql statement
dbbyte
= sqlstmt
getBytes();
sqlstmt = newString(dbbyte
”iso
”);
_stmt = _con
createStatement();
_stmt
executeUpdate(sqlstmt);
……
問題如果客戶機上存在 CLASSPATH 指向 JDK 的 CLASSESZIP 時(稱為 A 情況)上述程序代碼可正確執行但是如果客戶機只有浏覽器而沒有 JDK 和 CLASSPATH 時(稱為 B 情況)則漢字無法正確轉換
我們的分析
經過測試在 A 情況下程序運行時系統的缺省編碼方式為 GBK 或者 GB 在 B 情況下程序啟動時浏覽器的 JAVA 控制台中出現如下錯誤信息
Cant find resource for sunawtwindowsawtLocalization_zh_CN
然後系統的缺省編碼方式為“”
如果在轉換字符串時不采用系統缺省編碼方式而是直接采用 “GBK” 或“GB”則在 A 情況下程序仍然可正常運行在 B 情況下系統出現錯誤
UnsupportedEncodingException
在客戶機上把 JDK 的 CLASSESZIP 解壓後放在另一個目錄中 CLASSPATH 只包含該目錄然後一邊逐步刪除該目錄中的 CLASS 文件另一邊運行測試程序最後發現在一千多個 CLASS 文件中只有一個是必不可少的該文件是
sunioCharToByteDoubleByteclass
將該文件拷到服務器端和其它的類放在一起並在程序的開頭 IMPORT 它在 B 情況下程序仍然無法正常運行
在 A 情況下如果在 CLASSPTH 中去掉 sunioCharToByteDoubleByteclass 則程序運行時測得默認編碼方式為“”否則為 “GBK” 或 “GB”
如果 JDK 的版本為以上的話在 B 情況下遇到的問題得到了很好的解決測試的步驟同上有興趣的讀者可以嘗試一下
Java 中文問題的根源分析及解決
在簡體中文 MS Windows + JDK 下可以用 SystemgetProperties() 得到 Java 運行環境的一些基本屬性類 PoorChinese 可以幫助我們得到這些屬性
類 PoorChinese 的源代碼
public class PoorChinese {}
執行 java PoorChinese 後我們會得到:
系統變量 fileencoding 的值為 GBK userlanguage 的值為 zh userregion 的值為 CN 這些系統變量的值決定了系統默認的編碼方式是 GBK
在上述系統中下面的代碼將 GB 文件轉換成 Big 文件它們能夠幫助我們理解 Java 中漢字編碼的轉化:
import java
io
*;
import java
util
*;
public class gb
big
{
static int iCharNum=
;
public static void main(String[] args) {
System
out
println("Input GB
file
output Big
file
");
if (args
length!=
)
{
System
err
println("Usage: jview gb
big
gbfile big
file");
System
exit(
);
String inputString = readInput(args[
]);
writeOutput(inputString
args[
]);
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
"Big
");
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
"GB
");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in
read()) >
)
{
iCharNum +=
;buffer
append((char)ch);
}
in
close();
return buffer
toString();
}
catch (IOException e)
{
e
printStackTrace();
return null;
}
}
}
編碼轉化的過程如下
GB>Unicode>Big
執行 java gbbig gbtxt bigtxt 如果 gbtxt 的內容是“今天星期三”則得到的文件 bigtxt 中的字符能夠正確顯示而如果 gbtxt 的內容是“情人節快樂”則得到的文件 bigtxt 中對應於“節”和“樂”的字符都是符號“?”(xF)可見 sunioByteToCharGB 和 sunioCharToByteBig 這兩個基本類並沒有編好
正如上例一樣 Java 的基本類也可能存在問題由於國際化的工作並不是在國內完成的所以在這些基本類發布之前沒有經過嚴格的測試所以對中文字符的支持並不像 Java Soft 所聲稱的那樣完美前不久我的一位技術上的朋友發信給我說他終於找到了 Java Servlet 中文問題的根源兩周以來他一直為 Java Servlet 的中文問題所困擾因為每面對一個含有中文字符的字符串都必須進行強制轉換才能夠得到正確的結果(這好象是大家公認的唯一的解決辦法)
後來他確實不想如此繼續安分下去了因為這樣的事情確實不應該是高級程序員所要做的工作他就找出 Servlet 解碼的源代碼進行分析因為他懷疑問題就出在解碼這部分經過四個小時的奮斗他終於找到了問題的根源所在原來他的懷疑是正確的 Servlet 的解碼部分完全沒有考慮雙字節直接把 %XX 當作一個字符(原來 Java Soft 也會犯這幺低級的錯誤!)
如果你對這個問題有興趣或者遇到了同樣的煩惱的話你可以按照他的步驟 對Servletjar 進行修改
找到源代碼 HttpUtils 中的 static private String parseName 在返回前將 sb(StringBuffer) 復制成 byte bs[] 然後 return new String(bs”GB”)作上述修改後就需要自己解碼了
HashTable form=HttpUtils
parseQueryString(request
getQueryString())或者
form=HttpUtils
parsePostData(……)
千萬別忘了編譯後放到 Servletjar 裡面
關於 Java 中文問題的總結
Java 編程語言成長於網絡世界這就要求 Java 對多國字符有很好的支持 Java 編程語言適應了計算的網絡化的需求為它能夠在網絡世界迅速成長奠定了堅實的基礎 Java 的締造者 (Java Soft) 已經考慮到 Java 編程語言對多國字符的支持只是現在的解決方案有很多缺陷在裡面需要我們付諸一些補償性的措施而世界標准化組織也在努力把人類所有的文字統一在一種編碼之中其中一種方案是 ISO 它用四個字節來表示一個字符當然在這種方案未被采用之前還是希望 Java Soft 能夠嚴格地測試它的產品為用戶帶來更多的方便
附一個用於從數據庫和網絡中取出 中文亂碼的處理函數入參是有問題的字符串出參是問題已經解決了的字符串
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/20425.html