盡管關於Java在處理中文字符時所存在的問題的討論已不乏其數,但由於Java技術涉及內容廣(J2EE包含了十幾種相關技術),技術供應商繁多,面向Java的Web服務器、應用服務器以及JDBC數據庫驅動等都沒有官方的標准,所以Java應用在處理中文時除了存在固有的問題外也會隨著選用的服務器、驅動程序的不同產生一些與平台相關的問題。也就是說,在處理中文問題時,Java代碼的可移植性打了折扣。
總的看來,Java的中文處理問題較為集中地出現在JSP技術應用和Java的數據庫訪問過程中。這是因為無論是JSP應用還是基於JDBC的數據庫訪問都涉及到了Java程序與另外一種應用系統的交互,這種交互不可避免的要求系統之間進行數據的交互和參數的傳遞,而Java處理中文出現問題的地方往往就是這些數據讀入和輸出的地方。
JSP程序所應該注意的中文問題
以Tomcat 3.2.1的JSP應用為例,一般遇到中文問題可以使用如下的編碼強制轉換函數進行內碼的轉換。
public static String toChinese(String strvalue)
{
try{
if(strvalue==null)
return null;
else
{
strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");
return strvalue;
}
}catch(Exception e){
return null;
}
}
注意,在使用該函數前,我們需要分析中文無法正確輸出的原因到底是什麼,而不能將所有的中文處理的問題都用這個方法來解決。例如,如果是由於忘記將JSP的輸出代碼定義為GB2312或GBK而產生的中文無法正確輸出就不能用這個函數來解決。一個好的習慣是在我們編寫每一個JSP頁面時都在文件的第一行定義程序所要輸出的字符集,如:
<%@ page contentType="text/html; charset=GBK" %>或<%@ page contentType="text/Html;
charset=GB2312" %>
對於一些不支持定義輸出的字符集的JSP版本,我們也可以作如下的設置:
<META HTTP-EQUIV="Content-Type" CONTENT="text/Html; charset=gb2312">
另外還需要注意的是,這個函數是用來解決那些確實出現了無法正確輸出中文的代碼,而不是一個通用的用來保證中文字符正確輸出的函數。由於中文字符無法正確的輸出或讀入的原因都是因為這個字符的編碼和系統缺省的字符集編碼(或者是應用所要輸出的字符集,二者一般情況下是相同的)的不同引起的,所以在應用該函數前我們必須確定我們所要讀入或輸出的字符的編碼到底與系統缺省的字符集編碼是否相同。
下面的例子將給出該函數的正確和錯誤使用的情況。例子所采用的JSP的系統為Tomcat 3.2.1,客戶端和服務器端的運行環境都是中文的Windows2000。
例1
<%@ page contentType="text/Html; charset=GBK" %>
<Html>
<head>
<title>
testJSP
</title>
</head>
<body>
<h1>
<%
class testChina extends Object{
public String toChinese(String strvalue)
{
try{
if(strvalue==null)
return null;
else
{
strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");
return strvalue;
}
}catch(Exception e){
return null;
}
}
public void test(){
}
}
testChina testC = new testChina();
String str1 =new String("這是一個對中文支持的測試".getBytes("GBK"));
String str2=new String("這是一個對中文支持的測試".getBytes("GBK"),"ISO-8859-1");
String str3 =new String(testC.toChinese(str2));
out.println("Begin<br>");
out.println("str1");
out.println(str1+"<br>");
out.println("str2");
out.println(str2+"<br>");
out.println("str3");
out.println(str3+"<br>");
out.println("End<br>");
System.getPropertIEs().list(System.out);
%>
</h1>
</body>
</Html>
我們知道,Java編程語言默認的編碼方式是UNICODE但Java編譯器所使用的字符集則是操作系統的默認字符集,中文的Windows是GBK,英文系統則是ISO-8895-1。對於例1來講,系統的默認字符集是GBK,JSP的輸出字符集也是GBK,二者是一致的。對於str1,我們令其采用系統默認的字符集編碼;對於str2我們刻意的將其轉換為ISO-8895-1編碼以產生中文無法正確輸出的結果;str3是testC類的toChinese函數的一個不正確用法,它將原本正確的字符輸出轉化成了與系統字符集不符合的字符編碼,反而引起了造成中文輸出的錯誤;str3則是testC類toChinese函數的一個正確用法,它將str2的字符輸出錯誤糾正了過來。所以我們一定要正確分析字符輸出不正常的原因再使用toChinese函數。那麼我們如何來區分那些字符可能出現問題呢?下面有幾個主要的原則需要注意:
1)主要考慮字符變量情況
由於變量的字符編碼形式較為隱蔽,多次的變量間數值的改變和運算可能會引起字符集的改變;在變量與頁面所提交數據的各種操作中,較容易發生不同編碼格式字符進行運算的情況。
2)注意字符的讀入,讀出
大部分字符的編碼格式與目標編碼格式發生沖突的情況是發生在字符的讀入和輸出過程。例如Form的提交,URL的得到以及控件內容的顯示(如List控件)等等。
3)必要的時候需要作測試
由於Java的中文問題的產生隨著Web服務器,浏覽器,運行環境和開發工具的不同都可能發生變化,所以為了更好的避免問題的發生,我們必須作一些針對性的測試。
當然解決Java中文問題的方法並不局限於強制編碼輸出這一種。我們也可以采用下面的方法來解決:
1)以javac -encoding big5 sourcefile.java 或者javac -encoding gb2312 sourcefile.Java的方式編譯源程序。
2)采用Java2 JDK的中文本地化版本(http://Java.sun.com/products/jdk/1.2/chinesejdk.Html),但該版本是一種非官方的版本,Sun公司並不保證它的升級。
數據庫訪問過程中的中文問題
經過了上面的討論,對於數據庫訪問過程中所存在的中文問題也就不太難理解了。
目前,大部分的JDBC驅動程序並不是針對中文系統來設計的(中文數據大都采用ISO-8859-1編碼方式),所以在數據讀寫過程中往往需要字符編碼的轉化。
如果系統運行在中文操作系統平台下,則:
1)中文字符的讀入,可以采用如下的代碼:
strChinese= new String(String(rs.getObject(j).toString().getBytes("ISO-8859-1"));
對於Win2000平台下,采用Weblogic 6.0所提供的JDBC驅動來讀入中文代碼可如下來編寫(例子中進行了字符運算):
Driver myDriver = (Driver) Class.forName("weblogic.jdbc.mssqlserver4.Driver").newInstance();
conn = myDriver.connect("jdbc:weblogic:mssqlserver4", props);
conn.setCatalog("labmanager");
Statement st = conn.createStatement();
file://execute a query
String testStr;
String testTempStr = new String() ;
testStr = new String(testTempStr.getBytes("ISO-8859-1"));//編碼轉化
DatabaseMetaData DBMetaData =conn.getMetaData();
ResultSet rs = DBMetaData.getTables(null, null,null,new String[]{"TABLE"} );
while (rs.next()){
for(int j=1; j<=rs.getMetaData().getColumnCount(); j++){
testStr = testStr +String(rs.getObject(j).toString().getBytes("ISO-8859-1"));
}
}
2)中文的輸出。中文的輸出與讀入正好是個逆過程。我們需要將字符的系統缺省編碼轉化為JDBC支持的ISO-8859-1編碼。代碼可以如下編寫:
tempBytes=strInput.getText().getBytes();
SQLstr=new String(tempBytes,”ISO-8859-1”);
需要注意的是,不同的JDBC驅動對相同的數據庫的支持並不同,而同一類JDBC驅動對不同的數據庫的支持也不相同,也就是說我們的字符轉化代碼在JDBC驅動改變的時候必須作必要的測試才能確定其是否工作正常,不然我們反而會變成了畫蛇添足。例如對於i-net 的Una 2000 Driver Version 2.03 for MS SQL Server,我們根本就不需要做任何的編碼轉化就可以實現中文的正常操作。但是,由於很多的JDBC的驅動並沒有明確的給出其對中文字符的支持情況,所以建議在使用JDBC時都作一下測試。
結論
事實上,Java中文處理之所以存在問題,其根本原因是由於被操作的中文字符(變量)的編碼格式與目標的編碼格式不同造成的,所有這些問題其實都是發生在字符的讀入、輸出過程中的,只要我們把握住這一環節,就可以更好的理解和處理Java的中文問題了。