JAVA中文編碼和中文字元長度問題和解決方案
from: http://115.47.70.85/RuanJianGongCheng/2011-04/2859.htm
REF:http://www.ibm.com/developerworks/cn/Java/j-lo-chinesecoding/
摘要:本文說明了Java對中文問題產生的原因,並給出了對中文問題的解決方案。同時引發出了對中英文混和的字串的長度問題,並且給出解決方案的實現。隨著Java應用的增多,有關Java的中文問題也慢慢曝露出來。作為一個跨平臺的網路程式語言,Java對於語言的處理,有自己獨特的方法,但也隨之帶來了有些問題。
1、Java中文問題的產生Java為了對全球的常用文字編碼系統進行處理,採用了Unicode字元編碼集。Unicode字元編碼集是一種重要的互動和顯示的通用字元編碼標準,常見的有UTF-8、UTF-16、UCS-2、UCS-4等。國際標準組織為中文、日文和韓文字元(即CJK大字符集)對應的資料區間主要是4E00-9FFF,每一個字元對應惟一的一個編碼。例如,“中文”這兩個字對應的Unicode碼分別是:0x4E2D、 0x6587。下面這段程式碼System.out.println((char)0x4E2D + "" + (char)0x6587 ) ;就可以打印出“中文”來。
我們通常使用的字元編碼是一種雙位元組字符集(DBCS)。它與Unicode的編碼機制有很大差別。Java語言的中文處理問題一般就是,如何將DBCS編碼的位元組串相互轉換為正確的Unicode編碼的字串。所有中文問題的出現都是因為位元組串沒有被正確轉換所至。
中文問題的出現一般都是在幾種不同語言的作業系統中互動資訊的時候出現的。
2、Java中文問題解決方案首先,請確保你的JDK的版本是穩定的新版本,這是正確處理Java中文問題的前提條件。
2.1、其他內碼和Unicode碼之間的轉換解決問題的根源在於正確的處理各種內碼和Unicode碼進行相互轉換。Java的String類提供了轉換方法,具體用法是new String( byte[] , encoding ) ,即為用指定的字元編碼方式轉換指定的位元組陣列生成一個新的String。
比如:String abc = new String ( "hi...中文".getBytes( "GB2312" ) , "GB2312" ) ;
其中,"hi...中文".getBytes( "GB2312" )是按照GB2312的字元編碼方式把該 String 轉換成位元組陣列。然後再按照GB2312的方式生成一個abc的String物件。
2.2、讓JDK用你指定的編碼方式編譯程式在用javac編譯程式時,編譯器會用系統的預設編碼來編譯Java程式。用如下命令編譯javac -encoding GB2312 Xxx.java,則是指定用GB2312的編碼來進行編譯。
2.3、JDBC中的中文問題JDBC(Java DataBase Connectivity)是Java程式訪問資料庫的一個統一的介面。JDBC在網路傳輸過程中,大多數會採用本地編碼格式來傳輸中文字元,例如中文字元“0x4175”會被轉成“0x41”和“0x75”進行傳輸。因此需要對 JDBC返回的字元以及要發給JDBC的字元進行轉換。當用JDBC向資料庫中插入資料和查詢資料時,則需要作編碼轉換。所以當應用程式訪問資料時,在入口和出口處都要作編碼轉換。對於中文資料,資料庫字元編碼的設定應當保證資料的完整性,比如GB2312、GBK、UTF-8 等都是可選的資料庫編碼。
比如:轉換成UTF-8進行傳輸
sqlstr1 = new String(sqlstr1.getBytes("GB2312")," "UTF-8");
轉換成GB2312碼進行顯示
sqlstr2 = new String(sqlstr2.getBytes("UTF-8"),"GB2312");
3、Java中文編碼失敗情況說明如果出現編碼失敗,在顯示時會出現兩種結果:“?”或者“□”。“?”表示轉碼錯誤;“□”表示轉碼失敗。如果出現“?”,只有追本溯源查詢問題所在才能解決問題;如果出現“□”,則表示可以在此基礎上進一步進行轉碼操作直到成功。
4、Java中文編碼帶來的字串長度問題Java的中文問題處理系統除了在顯示方面會出現問題外,還會對包含中文字元的字串的長度的判斷帶來一定的問題。在C語言中,一箇中文字元是2個位元組,而在Java程式中,中文字元的長度是根據編碼不同而不同的。下面的程式就可以看出問題所在。
測試程式如下,測試字串為“中文abc”,測試平臺為中文Win XP sp2。
public class ChineseCharacterTest
{
public static void main( String [] args ) throws Exception
{
//按iso8859-1編碼
String iso = new String( "中文abc".getBytes( "GB2312" ) , "ISO8859-1" );
//按GB2312編碼
String gb = new String( iso.getBytes( "ISO8859-1" ) , "gb2312" ) ;
//按utf-8編碼
String utf_8 = new String( iso.getBytes( "ISO8859-1" ) , "UTF-8" ) ;
//下面分別打印出編碼後的字串和長度
System.out.println( "iso is :" + iso + ", the length is:" + iso.length() ) ;
System.out.println( "gb is :" + gb + ", the length is :" + gb.length() ) ;
System.out.println( "utf-8 is:" + utf_8 + ", the length is :" + utf_8.length() ) ;
}
}
用GB2312編碼進行編譯程式,其執行結果是:
iso is :????????abc, the length is:7
gb is :中文abc, the length is :5
utf-8 is:????????abc, the length is :7
可以看出,在ISO8859-1和UTF-8中一箇中文字元按照2個長度單位處理,在GB編碼中一箇中文字元按照1個長度單位處理。這樣在進行字元擷取時就要相當注意這個問題。如果在ISO8859-1或UTF-8中一不小心將中文截去半個,將會出現致命的錯誤。
5、中文字元長度問題的解決方案為了更好的解決中文字元的長度,我們設計並實現了下面的方法。基本思想是根據中文編碼的編碼區間,判斷字元是否為中文字元,然後再作進一步的處理。
程式如下:
public static int getChineseLength( String name , String endcoding )
throws Exception{
int len = 0 ; //定義返回的字串長度
int j = 0 ;
//按照指定編碼得到byte[]
byte [] b_name = name.getBytes( endcoding ) ;
while ( true ){
short tmpst = (short) ( b_name[ j ] & 0xF0 ) ;
if ( tmpst >= 0xB0 ){
if ( tmpst < 0xC0 ){
j += 2 ;
len += 2 ;
}
else if ( ( tmpst == 0xC0 ) || ( tmpst == 0xD0 ) ){
j += 2 ;
len += 2 ;
}
else if ( tmpst == 0xE0 ){
j += 3 ;
len += 2 ;
}
else if ( tmpst == 0xF0 ){
short tmpst0 = (short) ( ( (short) b_name[ j ] ) & 0x0F ) ;
if ( tmpst0 == 0 ){
j += 4 ;
len += 2 ;
}
else if ( ( tmpst0 > 0 ) && ( tmpst0 < 12 ) ){
j += 5 ;
len += 2 ;
}
else if ( tmpst0 > 11 ){
j += 6 ;
len += 2 ;
}
}
}
else{
j += 1 ;
len += 1 ;
}
if ( j > b_name.length - 1 ){
break ;
}
}
return len ;
}
這個程式適合在中西文混和的情況下方便的得到字串的長度,並且可以得到長度相同的字串。
6、綜述綜上所述,我們給出了Java中文問題的原因和解決方案,而且說明了關於Java中文字元處理帶來的對字串長度的影響,並給出了實際解決方案。實踐證明,這種方法在各種專案工程中均很好的解決了各種問題,真正發揮出了Java跨平臺的優點。
參考資料:
[1] 《The Unicode Standard》 www.unicode.org
[2] Bill Venners 《Inside the Java Virtual Machine》 Second Edition
[3] Sun , J2SDK簡體中文補充包 , http://www.sun.com.cn