1. 程式人生 > >Java各種中文亂碼問題及解決方案

Java各種中文亂碼問題及解決方案

問題的根源

Java的核心和class檔案是基於unicode的,這使Java程式具有良好的跨平臺性,但也帶來了一些中文亂碼問題的麻煩。原因主要有兩方面,Java和JSP檔案本身編譯時產生的亂碼問題和Java程式於其他媒介互動產生的亂碼問題。

  首先Java(包括JSP)原始檔中很可能包含有中文,而Java和JSP原始檔的儲存方式是基於位元組流的,如果Java和JSP編譯成 class檔案過程中,使用的編碼方式與原始檔的編碼不一致,就會出現亂碼。基於這種亂碼,建議在Java檔案中儘量不要寫中文(註釋部分不參與編譯,寫中文沒關係),如果必須寫的話,儘量手動帶引數-encoding GBK或-encoding gb2312編譯;對於JSP,在檔案頭加上或基本上就能解決這類亂碼問題。

問題的種類

即Java程式與其他儲存媒介互動時產生的亂碼。很多儲存媒介,如資料庫,檔案,流等的儲存方式都是基於位元組流的,Java程式與這些媒介互動時就會發生字元(char)與位元組(byte)之間的轉換,具體情況如下:

(1)從頁面form提交資料到java程式 byte->char

(2)從java程式到頁面顯示 char-->byte

(3)從資料庫到java程式 byte-->char

(4)從java程式到資料庫 char-->byte

(5)從檔案到java程式 byte->char

(6)從java程式到檔案 char->byte

如果在以上轉換過程中使用的編碼方式與位元組原有的編碼不一致,很可能就會出現亂碼。

基礎——瞭解字符集

(1)ASCII

    ASCII碼是7位編碼,編碼範圍是0x00-0x7F。ASCII字符集包括英文字母、阿拉伯數字和標點符號等字元。其中0x00-0x20和0x7F共33個控制字元。

    只支援ASCII碼的系統會忽略每個位元組的最高位,只認為低7位是有效位。HZ字元編碼就是早期為了在只支援7位ASCII系統中傳輸中文而設計的編碼。早期很多郵件系統也只支援ASCII編碼,為了傳輸中文郵件必須使用BASE64或者其他編碼方式。

(2)GB2312

GB2312 是基於區位碼設計的,區位碼把編碼表分為94個區,每個區對應94個位,每個字元的區號和位號組合起來就是該漢字的區位碼。區位碼一般用10進位制數來表示,如1601就表示16區1位,對應的字元是“啊”。在區位碼的區號和位號上分別加上0xA0就得到了GB2312編碼。

區位碼中01-09區是符號、數字區,16-87區是漢字區,10-15和88-94是未定義的空白區。它將收錄的漢字分成兩級:第一級是常用漢字計 3755個,置於16-55區,按漢語拼音字母/筆形順序排列;第二級漢字是次常用漢字計3008個,置於56-87區,按部首/筆畫順序排列。一級漢字 是按照拼音排序的,這個就可以得到某個拼音在一級漢字區位中的範圍,很多根據漢字可以得到拼音的程式就是根據這個原理編寫的。

GB2312字符集中除常用簡體漢字字元外還包括希臘字母、日文平假名及片假名字母、俄語西裡爾字母等字元,未收錄繁體中文漢字和一些生僻字。可以用繁體漢字測試某些系統是不是隻支援GB2312編碼。

區位碼更應該認為是字符集的定義,定義了所收錄的字元和字元位置,而GB2312及EUC-CN是實際計算機環境中支援這種字符集的編碼。HZ和ISO-2022-CN是對應區位碼字符集的另外兩種編碼,都是用7位編碼空間來支援漢字。區位碼和GB2312編碼的關係有點像 Unicode和UTF-8。

(3)GBK

GBK 編碼是GB2312編碼的超集,向下完全相容GB2312,同時GBK收錄了Unicode基本多文種平面中的所有CJK漢字。同 GB2312一樣,GBK也支援希臘字母、日文假名字母、俄語字母等字元,但不支援韓語中的表音字元(非漢字字元)。GBK還收錄了GB2312不包含的漢字部首符號、豎排標點符號等字元。

GBK編碼是中國大陸制訂的、等同於UCS的新的中文編碼擴充套件國家標準。GBK工作小組於1995年10月,同年12月完成GBK規範。該編碼標準相容GB2312,共收錄漢字21003個、符號883個,並提供1894個造字碼位,簡、繁體字融於一庫。

(4)GB18030

 GB18030編碼向下相容GBK和GB2312,相容的含義是不僅字元相容,而且相同字元的編碼也相同。GB18030收錄了所有Unicode3.1中的字元,包括中國少數民族字元,GBK不支援的韓文字元等等,也可以說是世界大多民族的文字元號都被收錄在內。

  GBK和GB2312都是雙位元組等寬編碼,如果算上和ASCII相容所支援的單位元組,也可以理解為是單位元組和雙位元組混合的變長編碼。GB18030編碼是變長編碼,有單位元組、雙位元組和四位元組三種方式。

GB18030 的單位元組編碼範圍是0x00-0x7F,完全等同與ASCII;雙位元組編碼的範圍和GBK相同,高位元組是0x81-0xFE,低位元組的編碼範圍是 0x40-0x7E和0x80-FE;四位元組編碼中第一、三位元組的編碼範圍是0x81-0xFE,二、四位元組是0x30-0x39。

 Windows 中CP936內碼表使用0x80來表示歐元符號,而在GB18030編碼中沒有使用0x80編碼位,用其他位置來表示歐元符號。這可以理解為是 GB18030向下相容性上的一點小問題;也可以理解為0x80是CP936對GBK的擴充套件,而GB18030只是和GBK相容良好。

(5)unicode

  每一種語言的不同的編碼頁,增加了那些需要支援不同語言的軟體的複雜度。因而人們制定了一個世界標準,叫做unicode。unicode為每個字元提供 了唯一的特定數值,不論在什麼平臺上、不論在什麼軟體中,也不論什麼語言。也就是說,它世界上使用的所有字元都列出來,並給每一個字元一個唯一特定數值。
   Unicode的最初目標,是用1個16位的編碼來為超過65000字元提供對映。但這還不夠,它不能覆蓋全部歷史上的文字,也不能解決傳輸的問題,尤其在那些基於網路的應用中。已有的軟體必須做大量的工作來程式16位的資料。
  因此,Unicode用一些基本的保留字元制定了三套編碼方式。它們分別是UTF-8,UTF-16和UTF-32。正如名字所示,在UTF-8中,字元是 以8位序列來編碼的,用一個或幾個位元組來表示一個字元。這種方式的最大好處,是UTF-8保留了ASCII字元的編碼做為它的一部分,例如,在UTF-8 和ASCII中,’A’的編碼都是0x41.

UTF-16和UTF-32分別是Unicode的16位和32位編碼方式。考慮到最初的目的,通常說的Unicode就是指UTF-16。在討論Unicode時,搞清楚哪種編碼方式非常重要。

(6)UTF-8

    Unicode Transformation Format-8bit,允許含BOM,但通常不含BOM。是用以解決國際上字元的一種多位元組編碼,它對英文使用8位(即一個位元組),中文使用24位(三個位元組)來編碼。UTF-8包含全世界所有國家需要用到的字元,是國際編碼,通用性強。UTF-8編碼的文字可以在各國支援UTF8字符集的瀏覽器上顯 示。如,如果是UTF8編碼,則在外國人的英文IE上也能顯示中文,他們無需下載IE的中文語言支援包。

   GBK的文字編碼是用雙位元組來表示的,即不論中、英文字元均使用雙位元組來表示,為了區分中文,將其最高位都設定成1。GBK包含全部中文字元,是國家編碼,通用性比UTF8差,不過UTF8佔用的資料庫比GBD大。

   GBK、GB2312等與UTF8之間都必須通過Unicode編碼才能相互轉換:

    GBK、GB2312--Unicode--UTF8

    UTF8--Unicode--GBK、GB2312

對於一個網站、論壇來說,如果英文字元較多,則建議使用UTF-8節省空間。不過現在很多論壇的外掛一般只支援GBK。

解決方法

(1)JSP頁面顯示亂碼

這種亂碼的原因是應為沒有在頁面裡指定使用的字符集編碼,解決方法:只要在頁面開始地方用下面程式碼指定字符集編碼即可

(2)表單提交中文時出現亂碼

JSP獲取頁面引數時一般採用系統預設的編碼方式,如果頁面引數的編碼型別和系統預設的編碼型別不一致,很可能就會出現亂碼。解決這類亂碼問題的基本方法是在頁面獲取引數之前,強制指定request獲取引數的編碼方式:request.setCharacterEncoding("GBK")或 request.setCharacterEncoding("gb2312")。

當然,使用下列方式亦可:、

String name = request.getParameter(“name”);

name = new String(name.getBytes(“iso-8859-1”),”gb2312”);

如果在JSP將變數輸出到頁面時出現了亂碼,可以通過設定

response.setContentType("text/html;charset=GBK")

response.setContentType("text/html;charset=gb2312")解決。

(3)資料庫連接出現亂碼

這種亂碼會使你插入資料庫的中文變成亂碼,或者讀出顯示時也是亂碼,解決方法如下:

  在資料庫連線字串中加入編碼字符集

  String Url="jdbc:mysql://localhost/database? user=root&password=root&useUnicode=true&characterEncoding=GB2312";
  

(4)資料庫的顯示亂碼

第一步:寫一個轉換字串的公共類

  
  

public class StringConvert {

     

    public static String Convert(String s)

  

    {

  

       String gb;

  

       try{

  

       if(s.equals("") || s == null){

  

           return "";

  

       }

  

       else{

  

           s   = s.trim();

  

       gb = new String(s.getBytes("ISO-8859-1"),"GBK");

  

       return gb;

  

           }

  

       }

  

       catch(Exception e){

  

       System.err.print("編碼轉換錯誤:"+e.getMessage());

  

       return "";

  

       }

  

    }

  

}

     

第二步:解決MySQL的中文問題

方法1、

開啟MySQL客戶端(MySQL command Line Client),設定以下三項:

  
  

        set character_set_client=gbk;

  

          set character_set_results=gbk;

  

          set character_set_connection=gbk;

  

 之後,再將MySQL安裝目錄下的my.ini檔案中的:default-character-set的值設為gbk(注意有兩處需要修改)。

 注1:這種方法有個缺陷就是,修改後,在新建表中的亂碼消失了,但是在原先的表中卻又出現了亂碼;解的辦法是,先將原先的表中的資料匯出,修改後,再重建表,最後再將資料匯入。

 注2:有關character_set_client等等的作用,

方法2、

 在安裝MySQL的時候稍微留心一下編碼的設定,將其設定為GBK一切OK,這樣就省去了以後的麻煩。