深入剖析Java編程中的中文問題及建議最優解決方案

來源:互聯網
上載者:User
1、中文問題的來源 
    電腦最初的作業系統支援的編碼是單位元組的字元編碼,於是,在電腦中一切處理常式最初都是以單位元組編碼的英文為準進行處理。隨著電腦的發展,為了適應世界其它民族的語言(當然包括我們的漢字),人們提出了UNICODE編碼,它採用雙位元組編碼,相容英文字元和其它民族的雙位元組字元編碼,所以,目前,大多數國際性的軟體內部均採用UNICODE編碼,在軟體運行時,它獲得本地支援系統(多數時間是作業系統)預設支援的編碼格式,然後再將軟體內部的UNICODE轉化為本地系統預設支援的格式顯示出來。Java的JDK和JVM即是如此,我這裡說的JDK是指國際版的JDK,我們大多數程式員使用的是國際化的JDK版本,以下所有的JDK均指國際化的JDK版本。我們的漢字是雙位元組編碼語言,為了能讓電腦處理中文,我們自己制定的gb2312、GBK、GBK2K等標準以適應電腦處理的需求。所以,大部分的作業系統為了適應我們處理中文的需求,均定製有中文作業系統,它們採用的是GBK,GB2312編碼格式以正確顯示我們的漢字。如:中文Win2K預設採用的是GBK編碼顯示,在中文WIN2k中儲存檔案時預設採用的儲存檔案的編碼格式也是GBK的,即,所有在中文WIN2K中儲存的檔案它的內部編碼預設均採用GBK編碼,注意:GBK是在GB2312基礎上擴充來的。
    由於Java語言內部採用UNICODE編碼,所以在JAVA程式運行時,就存在著一個從UNICODE編碼和對應的作業系統及瀏覽器支援的編碼格式轉換輸入、輸出的問題,這個轉換過程有著一系列的步驟,如果其中任何一步出錯,則顯示出來的漢字就會出是亂碼,這就是我們常見的JAVA中文問題。
    同時,Java是一個跨平台的程式設計語言,也即我們編寫的程式不僅能在中文windows上運行,也能在中文Linux等系統上運行,同時也要求能在英文等系統上運行(我們經常看到有人把在中文win2k上編寫的JAVA程式,移植到英文Linux上運行)。這種移植操作也會帶來中文問題。
    還有,有人使用英文的作業系統和英文的IE等瀏覽器,來運行帶中文字元的程式和瀏覽中文網頁,它們本身就不支援中文,也會帶來中文問題。
    有,幾乎所有的瀏覽器預設在傳遞參數時都是以UTF-8編碼格式來傳遞,而不是按中文編碼傳遞,所以,傳遞中文參數時也會有問題,從而帶來亂碼現象。
    總之,以上幾個方面是JAVA中的中文問題的主要來源,我們把以上原因造成的程式不能正確運行而產生的問題稱作:JAVA中文問題。
2、JAVA編碼轉換的詳細過程 
    我們常見的JAVA程式包括以下類別:
     *直接在console上啟動並執行類(包括可視化介面的類)
     *JSP代碼類(註:JSP是Servlets類的變型)
     *Servelets類
     *EJB類
     *其它不可以直接啟動並執行支援類
    這些類檔案中,都有可能含有中文字串,並且我們常用前三類JAVA程式和使用者直接互動,用於輸出和輸入字元,如:我們在JSP和Servlet中得到用戶端送來的字元,這些字元也包括中文字元。無論這些JAVA類的作用如何,這些JAVA程式的生命週期都是這樣的:
    *編程人員在一定的作業系統上選擇一個合適的編輯軟體來實現來源程式代碼並以.java副檔名儲存在作業系統中,例如我們在中文win2k中用記事本編輯一個java來源程式;
     *編程人員用JDK中的javac.exe來編譯這些原始碼,形成.class類(JSP檔案是由容器調用JDK來編譯的);
     *直接運行這些類或將這些類布署到WEB容器中去運行,並輸出結果。
    那麼,在這些過程中,JDK和JVM是如何將這些檔案如何編碼和解碼並啟動並執行呢?
    這裡,我們以中文win2k作業系統為例說明JAVA類是如何來編碼和被解碼的。
    第一步,我們在中文win2k中用編輯軟體如記事本編寫一個Java來源程式檔案(包括以上五類JAVA程式),程式檔案在儲存時預設採用了作業系統預設支援GBK編碼格式(作業系統預設支援的格式為file.encoding格式)形成了一個.java檔案,也即,java程式在被編譯前,我們的JAVA來源程式檔案是採用作業系統預設支援的file.encoding編碼格式儲存的,java來源程式中含有中文資訊字元和英文程式碼;要查看系統的file.encoding參數,可以用以下代碼:
public class ShowSystemDefaultEncoding {
public static void main(String[] args) {
String encoding = System.getProperty("file.encoding");
System.out.println(encoding);
}}
    第二步,我們用JDK的javac.exe檔案編譯我們的Java來源程式,由於JDK是國際版的,在編譯的時候,如果我們沒有用-encoding參數指定我們的JAVA來源程式的編碼格式,則javac.exe首先獲得我們作業系統預設採用的編碼格式,也即在編譯java程式時,若我們不指定來源程式檔案的編碼格式,JDK首先獲得作業系統的file.encoding參數(它儲存的就是作業系統預設的編碼格式,如WIN2k,它的值為GBK),然後JDK就把我們的java來源程式從file.encoding編碼格式轉化為JAVA內部預設的UNICODE格式放入記憶體中。然後,javac把轉換後的unicode格式的檔案進行編譯成.class類檔案,此時.class檔案是UNICODE編碼的,它暫放在記憶體中,緊接著,JDK將此以UNICODE編碼的編譯後的class檔案儲存到我們的作業系統中形成我們見到的.class檔案。對我們來說,我們最終獲得的.class檔案是內容以UNICODE編碼格式儲存的類檔案,它內部包含我們來源程式中的中文字串,只不過此時它己經由file.encoding格式轉化為UNICODE格式了。
    這一步中,對於JSP來源程式檔案是不同的,對於JSP,這個過程是這樣的:即WEB容器調用JSP編譯器,JSP編譯器先查看JSP檔案中是否設定有檔案編碼格式,如果JSP檔案中沒有設定JSP檔案的編碼格式,則JSP編譯器調用JDK先把JSP檔案用JVM預設的字元編碼格式(也即WEB容器所在的作業系統的預設的file.encoding)轉化為臨時的Servlet類,然後再把它編譯成UNICODE格式的class類,並儲存在臨時檔案夾中。如:在中文win2k上,WEB容器就把JSP檔案從GBK編碼格式轉化為UNICODE格式,然後編譯成臨時儲存的Servlet類,以響應使用者的請求。
    第三步,運行第二步編譯出來的類,分為三種情況:
    A、 直接在console上啟動並執行類
    B、 EJB類和不可以直接啟動並執行支援類(如JavaBean類)
    C、 JSP代碼和Servlet類
    D、 JAVA程式和資料庫之間
    下面我們分這四種情況來看。
    A、直接在console上啟動並執行類
    這種情況,運行該類首先需要JVM支援,即作業系統中必須安裝有JRE。運行過程是這樣的:首先java啟動JVM,此時JVM讀出作業系統中儲存的class檔案並把內容讀入記憶體中,此時記憶體中為UNICODE格式的class類,然後JVM運行它,如果此時此類需要接收使用者輸入,則類會預設用file.encoding編碼格式對使用者輸入的串進行編碼並轉化為unicode儲存入記憶體(使用者可以設定輸入資料流的編碼格式)。程式運行後,產生的字串(UNICODE編碼的)再回交給JVM,最後JRE把此字串再轉化為file.encoding格式(使用者可以設定輸出資料流的編碼格式)傳遞給作業系統顯示介面並輸出到介面上。
    以上每一步的轉化都需要正確的編碼格式轉化,才能最終不出現亂碼現象。
    B、EJB類和不可以直接啟動並執行支援類(如JavaBean類)
    由於EJB類和不可以直接啟動並執行支援類,它們一般不與使用者直接互動輸入和輸出,它們常常與其它的類進行互動輸入和輸出,所以它們在第二步被編譯後,就形成了內容是UNICODE編碼的類儲存在作業系統中了,以後只要它與其它的類之間的互動在參數傳遞過程中沒有丟失,則它就會正確的運行。
    C、JSP代碼和Servlet類
    經過第二步後,JSP檔案也被轉化為Servlets類檔案,只不過它不像標準的Servlets一校存在於classes目錄中,它存在於WEB容器的臨時目錄中,故這一步中我們也把它做為Servlets來看。
    對於Servlets,用戶端請求它時,WEB容器調用它的JVM來運行Servlet,首先,JVM把Servlet的class類從系統中讀出並裝入記憶體中,記憶體中是以UNICODE編碼的Servlet類的代碼,然後JVM在記憶體中運行該Servlet類,如果Servlet在啟動並執行過程中,需要接受從用戶端傳來的字元如:表單輸入的值和URL中傳入的值,此時如果程式中沒有設定接受參數時採用的編碼格式,則WEB容器會預設採用ISO-8859-1編碼格式來接受傳入的值並在JVM中轉化為UNICODE格式的儲存在WEB容器的記憶體中。Servlet運行後產生輸出,輸出的字串是UNICODE格式的,緊接著,容器將Servlet運行產生的UNICODE格式的串(如html文法,使用者輸出的串等)直接發送到用戶端瀏覽器上並輸出給使用者,如果此時指定了發送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,如果沒有指定,則預設按ISO-8859-1編碼發送到客戶的瀏覽器上。
    D、Java程式和資料庫之間
    對於幾乎所有資料庫的JDBC驅動程式,預設的在JAVA程式和資料庫之間傳遞資料都是以ISO-8859-1為預設編碼格式的,所以,我們的程式在向資料庫記憶體儲包含中文的資料時,JDBC首先是把程式內部的UNICODE編碼格式的資料轉化為ISO-8859-1的格式,然後傳遞到資料庫中,在資料庫儲存資料時,它預設即以ISO-8859-1儲存,所以,這是為什麼我們常常在資料庫中讀出的中文資料是亂碼。

    3、分析常見的JAVA中文問題幾個必須清楚的原則
    首先,經過上面的詳細分析,我們可以清晰地看到,任何JAVA程式的生命期中,其編碼轉換的關鍵過程是在於:最初編譯成class檔案的轉碼和最終向使用者輸出的轉碼過程。
    其次,我們必須瞭解JAVA在編譯時間支援的、常用的編碼格式有以下幾種:
    *ISO-8859-1,8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等編碼
    *Cp1252,美國英語編碼,同ANSI標準編碼
    *UTF-8,同unicode編碼
    *GB2312,同gb2312-80,gb2312-1980等編碼
    *GBK , 同MS936,它是gb2312的擴充
    及其它的編碼,如韓文、日文、繁體中文等。同時,我們要注意這些編碼間的相容關體系如下:
    unicode和UTF-8編碼是一一對應的關係。GB2312可以認為是GBK的子集,即GBK編碼是在gb2312上擴充來的。同時,GBK編碼包含了20902個漢字,編碼範圍為:0x8140-0xfefe,所有的字元可以一一對應到UNICODE2.0中來。
    再次,對於放在作業系統中的.java來源程式檔案,在編譯時間,我們可以指定它內容的編碼格式,具體來說用-encoding來指定。注意:如果來源程式中含有中文字元,而你用-encoding指定為其它的編碼字元,顯然是要出錯的。用-encoding指定源檔案的編碼方式為GBK或gb2312,無論我們在什麼系統上編譯含有中文字元的JAVA來源程式都不會有問題,它都會正確地將中文轉化為UNICODE儲存在class檔案中。
    
然後,我們必須清楚,幾乎所有的WEB容器在其內部預設的字元編碼格式都是以ISO-8859-1為預設值的,同時,幾乎所有的瀏覽器在傳遞參數時都是預設以UTF-8的方式來傳遞參數的。所以,雖然我們的Java源檔案在出入口的地方指定了正確的編碼方式,但其在容器內部運行時還是以ISO-8859-1來處理的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.