隨筆 - 1  文章 - 0  trackbacks - 0
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          搜索

          •  

          最新評論

           Abstract:本文深入分析了Java程序設計中Java編譯器對java源文件和JVM對class類文件的編碼/解碼過程,通過此過程的解析透視出了Java編程中中文問題產(chǎn)生的根本原因,最后給出了建議的最優(yōu)化的解決Java中文問題的方法。?

            1、中文問題的來源

          ??? 計算機最初的操作系統(tǒng)支持的編碼是單字節(jié)的字符編碼,于是,在計算機中一切處理程序最初都是以單字節(jié)編碼的英文為準進行處理。隨著計算機的發(fā)展,為了適應世界其它民族的語言(當然包括我們的漢字),人們提出了UNICODE編碼,它采用雙字節(jié)編碼,兼容英文字符和其它民族的雙字節(jié)字符編碼,所以,目前,大多數(shù)國際***的軟件內(nèi)部均采用UNICODE編碼,在軟件運行時,它獲得本地支持系統(tǒng)(多數(shù)時間是操作系統(tǒng))默認支持的編碼格式,然后再將軟件內(nèi)部的 UNICODE轉(zhuǎn)化為本地系統(tǒng)默認支持的格式顯示出來。Java的JDK和JVM即是如此,我這里說的JDK是指國際版的JDK,我們大多數(shù)程序員使用的是國際化的JDK版本,以下所有的JDK均指國際化的JDK版本。我們的漢字是雙字節(jié)編碼語言,為了能讓計算機處理中文,我們自己制定的gb2312、 GBK、GBK2K等標準以適應計算機處理的需求。所以,大部分的操作系統(tǒng)為了適應我們處理中文的需求,均定制有中文操作系統(tǒng),它們采用的是GBK, GB2312編碼格式以正確顯示我們的漢字。如:中文Win2K默認采用的是GBK編碼顯示,在中文WIN2k中保存文件時默認采用的保存文件的編碼格式也是GBK的,即,所有在中文WIN2K中保存的文件它的內(nèi)部編碼默認均采用GBK編碼,注意:GBK是在GB2312基礎上擴充來的。

          ??? 由于Java語言內(nèi)部采用UNICODE編碼,所以在JAVA程序運行時,就存在著一個從UNICODE編碼和對應的操作系統(tǒng)及瀏覽器支持的編碼格式轉(zhuǎn)換輸入、輸出的問題,這個轉(zhuǎn)換過程有著一系列的步驟,如果其中任何一步出錯,則顯示出來的漢字就會出是亂碼,這就是我們常見的JAVA中文問題。

          ??? 同時,Java是一個跨平臺的編程語言,也即我們編寫的程序不僅能在中文windows上運行,也能在中文Linux等系統(tǒng)上運行,同時也要求能在英文等系統(tǒng)上運行(我們經(jīng)??吹接腥税言谥形膚in2k上編寫的JAVA程序,移植到英文Linux上運行)。這種移植操作也會帶來中文問題。

          ??? 還有,有人使用英文的操作系統(tǒng)和英文的IE等瀏覽器,來運行帶中文字符的程序和瀏覽中文網(wǎng)頁,它們本身就不支持中文,也會帶來中文問題。

          ??? 幾乎所有的瀏覽器默認在傳遞參數(shù)時都是以UTF-8編碼格式來傳遞,而不是按中文編碼傳遞,所以,傳遞中文參數(shù)時也會有問題,從而帶來亂碼現(xiàn)象。

          ??? 總之,以上幾個方面是JAVA中的中文問題的主要來源,我們把以上原因造成的程序不能正確運行而產(chǎn)生的問題稱作:JAVA中文問題。

            2、JAVA編碼轉(zhuǎn)換的詳細過程

          ??? 我們常見的JAVA程序包括以下類別:
          ???? *直接在console上運行的類(包括可視化界面的類)
          ???? *JSP代碼類(注:JSP是Servlets類的變型)
          ???? *Servelets類
          ???? *EJB類
          ???? *其它不可以直接運行的支持類

          ??? 這些類文件中,都有可能含有中文字符串,并且我們常用前三類JAVA程序和用戶直接交互,用于輸出和輸入字符,如:我們在JSP和Servlet中得到客戶端送來的字符,這些字符也包括中文字符。無論這些JAVA類的作用如何,這些JAVA程序的生命周期都是這樣的:

          ??? *編程人員在一定的操作系統(tǒng)上選擇一個合適的編輯軟件來實現(xiàn)源程序代碼并以.java擴展名保存在操作系統(tǒng)中,例如我們在中文win2k中用記事本編輯一個java源程序;
          ???? *編程人員用JDK中的javac.exe來編譯這些源代碼,形成.class類(JSP文件是由容器調(diào)用JDK來編譯的);
          ???? *直接運行這些類或?qū)⑦@些類布署到WEB容器中去運行,并輸出結(jié)果。
          ??? 那么,在這些過程中,JDK和JVM是如何將這些文件如何編碼和解碼并運行的呢?

          這里,我們以中文win2k操作系統(tǒng)為例說明JAVA類是如何來編碼和被解碼的。

          ??? 第一步,我們在中文win2k中用編輯軟件如記事本編寫一個Java源程序文件(包括以上五類JAVA 程序),程序文件在保存時默認采用了操作系統(tǒng)默認支持GBK編碼格式(操作系統(tǒng)默認支持的格式為file.encoding格式)形成了一個.java文件,也即,java程序在被編譯前,我們的JAVA源程序文件是采用操作系統(tǒng)默認支持的file.encoding編碼格式保存的,java源程序中含有中文信息字符和英文程序代碼;要查看系統(tǒng)的file.encoding參數(shù),可以用以下代碼:
            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參數(shù)指定我們的 JAVA源程序的編碼格式,則javac.exe首先獲得我們操作系統(tǒng)默認采用的編碼格式,也即在編譯java程序時,若我們不指定源程序文件的編碼格式,JDK首先獲得操作系統(tǒng)的file.encoding參數(shù)(它保存的就是操作系統(tǒng)默認的編碼格式,如WIN2k,它的值為GBK),然后JDK就把我們的java源程序從file.encoding編碼格式轉(zhuǎn)化為JAVA內(nèi)部默認的 UNICODE格式放入內(nèi)存中。然后,javac把轉(zhuǎn)換后的unicode格式的文件進行編譯成.class類文件,此時.class文件是 UNICODE編碼的,它暫放在內(nèi)存中,緊接著,JDK將此以UNICODE編碼的編譯后的class文件保存到我們的操作系統(tǒng)中形成我們見到的. class文件。對我們來說,我們最終獲得的.class文件是內(nèi)容以UNICODE編碼格式保存的類文件,它內(nèi)部包含我們源程序中的中文字符串,只不過此時它己經(jīng)由file.encoding格式轉(zhuǎn)化為UNICODE格式了。

          ??? 這一步中,對于JSP源程序文件是不同的,對于JSP,這個過程是這樣的:即WEB容器調(diào)用JSP編譯器,JSP編譯器先查看JSP文件中是否設置有文件編碼格式,如果JSP文件中沒有設置JSP文件的編碼格式,則JSP編譯器調(diào)用JDK先把JSP文件用JVM默認的字符編碼格式(也即WEB容器所在的操作系統(tǒng)的默認的file.encoding)轉(zhuǎn)化為臨時的Servlet類,然后再把它編譯成UNICODE格式的class類,并保存在臨時文件夾中。如:在中文win2k上,WEB容器就把JSP文件從GBK編碼格式轉(zhuǎn)化為UNICODE格式,然后編譯成臨時保存的Servlet類,以響應用戶的請求。

          ??? 第三步,運行第二步編譯出來的類,分為三種情況:

          ??? A、 直接在console上運行的類
          ??? B、 EJB類和不可以直接運行的支持類(如JavaBean類)
          ??? C、 JSP代碼和Servlet類
          ??? D、 JAVA程序和數(shù)據(jù)庫之間
          ??? 下面我們分這四種情況來看。
          ??? A、直接在console上運行的類

          ??? 這種情況,運行該類首先需要JVM支持,即操作系統(tǒng)中必須安裝有JRE。運行過程是這樣的:首先java啟動JVM,此時JVM讀出操作系統(tǒng)中保存的 class文件并把內(nèi)容讀入內(nèi)存中,此時內(nèi)存中為UNICODE格式的class類,然后JVM運行它,如果此時此類需要接收用戶輸入,則類會默認用 file.encoding編碼格式對用戶輸入的串進行編碼并轉(zhuǎn)化為unicode保存入內(nèi)存(用戶可以設置輸入流的編碼格式)。程序運行后,產(chǎn)生的字符串(UNICODE編碼的)再回交給JVM,最后JRE把此字符串再轉(zhuǎn)化為file.encoding格式(用戶可以設置輸出流的編碼格式)傳遞給操作系統(tǒng)顯示接口并輸出到界面上。

          ??? 對于這種直接在console上運行的類,它的轉(zhuǎn)化過程可用圖1更加明確的表示出來:

          圖1

          以上每一步的轉(zhuǎn)化都需要正確的編碼格式轉(zhuǎn)化,才能最終不出現(xiàn)亂碼現(xiàn)象。

          ???
          B、EJB類和不可以直接運行的支持類(如JavaBean類)

          ??? 由于EJB類和不可以直接運行的支持類,它們一般不與用戶直接交互輸入和輸出,它們常常與其它的類進行交互輸入和輸出,所以它們在第二步被編譯后,就形成了內(nèi)容是UNICODE編碼的類保存在操作系統(tǒng)中了,以后只要它與其它的類之間的交互在參數(shù)傳遞過程中沒有丟失,則它就會正確的運行。
          這種EJB類和不可以直接運行的支持類, 它的轉(zhuǎn)化過程可用圖2更加明確的表示出來:

          圖2


          ??? C、JSP代碼和Servlet類

          ??? 經(jīng)過第二步后,JSP文件也被轉(zhuǎn)化為Servlets類文件,只不過它不像標準的Servlets一校存在于classes目錄中,它存在于WEB容器的臨時目錄中,故這一步中我們也把它做為Servlets來看。

          ??? 對于Servlets,客戶端請求它時,WEB容器調(diào)用它的JVM來運行Servlet,首先,JVM把Servlet的class類從系統(tǒng)中讀出并裝入內(nèi)存中,內(nèi)存中是以UNICODE編碼的Servlet類的代碼,然后JVM在內(nèi)存中運行該Servlet類,如果Servlet在運行的過程中,需要接受從客戶端傳來的字符如:表單輸入的值和URL中傳入的值,此時如果程序中沒有設定接受參數(shù)時采用的編碼格式,則WEB容器會默認采用ISO-8859- 1編碼格式來接受傳入的值并在JVM中轉(zhuǎn)化為UNICODE格式的保存在WEB容器的內(nèi)存中。Servlet運行后生成輸出,輸出的字符串是 UNICODE格式的,緊接著,容器將Servlet運行產(chǎn)生的UNICODE格式的串(如html語法,用戶輸出的串等)直接發(fā)送到客戶端瀏覽器上并輸出給用戶,如果此時指定了發(fā)送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,如果沒有指定,則默認按ISO-8859-1編碼發(fā)送到客戶的瀏覽器上。這種JSP代碼和Servlet類,它的轉(zhuǎn)化過程可用圖3更加明確地表示出來:

          圖3

          D、Java程序和數(shù)據(jù)庫之間

          ??? 對于幾乎所有數(shù)據(jù)庫的JDBC驅(qū)動程序,默認的在JAVA程序和數(shù)據(jù)庫之間傳遞數(shù)據(jù)都是以ISO-8859-1為默認編碼格式的,所以,我們的程序在向數(shù)據(jù)庫內(nèi)存儲包含中文的數(shù)據(jù)時,JDBC首先是把程序內(nèi)部的UNICODE編碼格式的數(shù)據(jù)轉(zhuǎn)化為ISO-8859-1的格式,然后傳遞到數(shù)據(jù)庫中,在數(shù)據(jù)庫保存數(shù)據(jù)時,它默認即以ISO-8859-1保存,所以,這是為什么我們常常在數(shù)據(jù)庫中讀出的中文數(shù)據(jù)是亂碼。
          ??? 對于JAVA程序和數(shù)據(jù)庫之間的數(shù)據(jù)傳遞,我們可以用圖4清晰地表示出來

          ??????????????????????????????????????????????????????????????????? 圖4


          ??? 3、分析常見的JAVA中文問題幾個必須清楚的原則

          ??? 首先,經(jīng)過上面的詳細分析,我們可以清晰地看到,任何JAVA程序的生命期中,其編碼轉(zhuǎn)換的關(guān)鍵過程是在于:最初編譯成class文件的轉(zhuǎn)碼和最終向用戶輸出的轉(zhuǎn)碼過程。
          ??? 其次,我們必須了解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的擴充
          ??? 及其它的編碼,如韓文、日文、繁體中文等。同時,我們要注意這些編碼間的兼容關(guān)體系如下:
          ??? unicode和UTF-8編碼是一一對應的關(guān)系。GB2312可以認為是GBK的子集,即GBK編碼是在gb2312上擴展來的。同時,GBK編碼包含了20902個漢字,編碼范圍為:0x8140-0xfefe,所有的字符可以一一對應到UNICODE2.0中來。

          ??? 再次,對于放在操作系統(tǒng)中的.java源程序文件,在編譯時,我們可以指定它內(nèi)容的編碼格式,具體來說用-encoding來指定。注意:如果源程序中含有中文字符,而你用-encoding指定為其它的編碼字符,顯然是要出錯的。用-encoding指定源文件的編碼方式為GBK或gb2312,無論我們在什么系統(tǒng)上編譯含有中文字符的JAVA源程序都不會有問題,它都會正確地將中文轉(zhuǎn)化為UNICODE存儲在class文件中。
          ????
          ??? 然后,我們必須清楚,幾乎所有的WEB容器在其內(nèi)部默認的字符編碼格式都是以ISO-8859-1為默認值的,同時,幾乎所有的瀏覽器在傳遞參數(shù)時都是默認以UTF-8的方式來傳遞參數(shù)的。所以,雖然我們的Java源文件在出入口的地方指定了正確的編碼方式,但其在容器內(nèi)部運行時還是以ISO-8859- 1來處理的。


          ? 4、中文問題的分類及其建議最優(yōu)解決辦法

          ??? 了解以上JAVA處理文件的原理之后,我們就可以提出了一套建議最優(yōu)的解決漢字問題的辦法。
          ??? 我們的目標是:我們在中文系統(tǒng)中編輯的含有中文字符串或進行中文處理的JAVA源程序經(jīng)編譯后可以移值到任何其它的
          操作系統(tǒng) 中正確運行,或拿到其它操作系統(tǒng)中編譯后能正確運行,能正確地傳遞中文和英文參數(shù),能正確地和 數(shù)據(jù)庫 交流中英文字符串。
          ??? 我們的具體思路是:在JAVA程序轉(zhuǎn)碼的入口和出口及JAVA程序同用戶有輸入輸出轉(zhuǎn)換的地方限制編碼方法使之正確即可。

          ??? 具體解決辦法如下:

          ??? 1、 針對直接在console上運行的類
          ??? 對于這種情況,我們建議在程序編寫時,如果需要從用戶端接收用戶的可能含有中文的輸入或含有中文的輸出,程序中應該采用字符流來處理輸入和輸出,具體來說,應用以下面向字符型節(jié)點流類型:
          ??? 對文件:FileReader,F(xiàn)ileWrieter
          ??????? 其字節(jié)型節(jié)點流類型為:FileInputStream,F(xiàn)ileOutputStream
          ??? 對內(nèi)存(數(shù)組):CharArrayReader,CharArrayWriter
          ??????? 其字節(jié)型節(jié)點流類型為:ByteArrayInputStream,ByteArrayOutputStream
          ??? 對內(nèi)存(字符串):StringReader,StringWriter
          ??? 對管道:PipedReader,PipedWriter
          ??????? 其字節(jié)型節(jié)點流類型為:PipedInputStream,PipedOutputStream
          ??? 同時,應該用以下面向字符型處理流來處理輸入和輸出:
          ??? BufferedWriter,BufferedReader
          ??????? 其字節(jié)型的處理流為:BufferedInputeStream,BufferedOutputStream
          ??? InputStreamReader,OutputStreamWriter
          ??? 其字節(jié)型的處理流為:DataInputStream,DataOutputStream
          ??? 其中InputStreamReader和InputStreamWriter用于將字節(jié)流按照指定的字符編碼集轉(zhuǎn)換到字符流,如:
          ??? InputStreamReader in = new InputStreamReader(System.in,"GB2312");
          ??? OutputStreamWriter out = new OutputStreamWriter (System.out,"GB2312");
          ??? 例如:采用如下的示例JAVA編碼就達到了要求:

          ??? //Read.java
          ??? import java.io.*;
          ??? public class Read {
          ??? public static void main(String[] args) throws IOException {
          ??? String str = "\n中文測試,這是內(nèi)部硬編碼的串"+"\ntest english character";
          ??? String strin= "";
          ??? BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //設置輸入接口按中文編碼
          ??? BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312")); //設置輸出接口按中文編碼
          ??? stdout.write("請輸入:");
          ??? stdout.flush();
          ??? strin = stdin.readLine();
          ??? stdout.write("這是從用戶輸入的串:"+strin);
          ??? stdout.write(str);
          ??? stdout.flush();
          ??? }}
          ??? 同時,在編譯程序時,我們用以下方式來進行:
          ??? javac -encoding gb2312 Read.java
          ??? 其運行結(jié)果如圖5所示:

          ??? 圖5
          2、 針對EJB類和不可以直接運行的支持類(如JavaBean類)

          ??? 由于這種類它們本身被其它的類調(diào)用,不直接與用戶交互,故對這種類來說,我們的建議的處理方式是內(nèi)部程序中應該采用字符流來處理程序內(nèi)部的中文字符串(具體如上面一節(jié)中一樣),同時,在編譯類時用-encoding gb2312參數(shù)指示源文件是中文格式編碼的即可。


          ??? 3、 針對Servlet類

          ??? 針對Servlet,我們建議用以下方法:(我建議將.java文件設置為UTF8編碼的。當然如果是用Eclipse的話,只要設置下就是了。對于數(shù)據(jù)庫,我以為編碼最好設置為UTF8,便于國際化?。盡可能的使用UTF8碼,? 千里草)

          ??? 在編譯Servlet類的源程序時,用-encoding指定編碼為GBK或GB2312,且在向用戶輸出時的編碼部分用response對象的 setContentType("text/html;charset=GBK");或gb2312來設置輸出編碼格式,同樣在接收用戶輸入時,我們用 request.setCharacterEncoding("GB2312");這樣無論我們的servlet類移植到什么操作系統(tǒng)中,只有客戶端的瀏覽器支持中文顯示,就可以正確顯示。如下是一個正確的示例:

          ??? //HelloWorld.java
          ??? package hello;
          ??? import java.io.*;
          ??? import javax.servlet.*;
          ??? import javax.servlet.http.*;
          ??? public class HelloWorld extends HttpServlet
          ??? {
          ??? public void init() throws ServletException { }
          ??? public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
          ??? {
          ??? request.setCharacterEncoding("GB2312"); //設置輸入編碼格式
          ??? response.setContentType("text/html;charset=GB2312"); //設置輸出編碼格式
          ??? PrintWriter out = response.getWriter(); //建議使用PrintWriter輸出
          ??? out.println("<hr>");
          ??? out.println("Hello World! This is created by Servlet!測試中文!");
          ??? out.println("<hr>");
          ??? }
          ??? public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
          ??? {
          ??? request.setCharacterEncoding("GB2312"); //設置輸入編碼格式
          ??? response.setContentType("text/html;charset=GB2312"); //設置輸出編碼格式
          ??? String name = request.getParameter("name");
          ??? String id = request.getParameter("id");
          ??? if(name==null) name="";
          ??? if(id==null) id="";
          ??? PrintWriter out = response.getWriter(); //建議使用PrintWriter輸出
          ??? out.println("<hr>");
          ??? out.println("你傳入的中文字串是:" + name);
          ??? out.println("<hr>你輸入的id是:" + id);
          ??? out.println("<hr>");
          ??? }
          ??? public void destroy() { }
          ??? }
          ??????? 請用javac -encoding gb2312 HelloWorld.java來編譯此程序。
          ??????? 測試此Servlet的程序如下所示:
          ??? <%@page contentType="text/html; charset=gb2312"%>
          ??? <%request.setCharacterEncoding("GB2312");%>
          ??? <html><head><title></title>
          ??? <Script language="JavaScript">
          ??? function Submit() {
          ??? //通過URL傳遞中文字符串值給Servlet
          ??? document.base.action = "./HelloWorld?name=中文";
          ??? document.base.method = "POST";
          ??? document.base.submit();
          ??? }
          ??? </Script>
          ??? </head>

          <body bgcolor="#FFFFFF" text="#000000" topmargin="5">
          ??? <form name="base" method = "POST" target="_self">
          ??? <input name="id" type="text" value="" size="30">
          ??? <a href = "JavaScript:Submit()">傳給Servlet</a>
          ??? </form></body></html>
          ??? 其運行結(jié)果如圖6所示:

          ??? 圖6
          ??? 4、 JAVA程序和數(shù)據(jù)庫之間

          ??? 為避免JAVA程序和數(shù)據(jù)庫之間數(shù)據(jù)傳遞出現(xiàn)亂碼現(xiàn)象,我們建議采用以下最優(yōu)方法來處理:
          ??? 1、 對于JAVA程序的處理方法按我們指定的方法處理。
          ??? 2、 把數(shù)據(jù)庫默認支持的編碼格式改為GBK或GB2312的。

          ??? 如:在mysql中,我們可以在配置文件my.ini中加入以下語句實現(xiàn):
          ??? 在[mysqld]區(qū)增加:
          ??? default-character-set=gbk
          ??? 并增加:
          ??? [client]
          ??? default-character-set=gbk
          ??? 在SQL Server2K中,我們可以將數(shù)據(jù)庫默認的語言設置為Simplified Chinese來達到目的。

          ??? 5、 針對JSP代碼

          ??? 由于JSP是在運行時,由WEB容器進行動態(tài)編譯的,如果我們沒有指定JSP源文件的編碼格式,則JSP編譯器會獲得服務器操作系統(tǒng)的 file.encoding值來對JSP文件編譯的,它在移植時最容易出問題,如在中文win2k中可以很好運行的jsp文件拿到英文linux中就不行,盡管客戶端都是一樣的,那是因為容器在編譯JSP文件時獲取的操作系統(tǒng)的編碼不同造成的(在中文wink中的file.encoding和在英文 Linux中file.encoding是不同的,且英文Linux的file.encoding對中文不支持,所以編譯出來的JSP類就會有問題)。 網(wǎng)絡 上討論的大多數(shù)是此類問題,多是因為JSP文件移植平臺時不能正確顯示的問題,對于這類問題,我們了解了JAVA中程序編碼轉(zhuǎn)換的原理,解決起來就容易多了。我們建議的解決辦法如下:

          ??? 1、我們要保證JSP向客戶端輸出時是采用中文編碼方式輸出的,即無論如何我們首先在我們的JSP源代編中加入以下一行:

          ??? < %@page contentType="text/html; charset=gb2312"%>
          ??? 2、為了讓JSP能正確獲得傳入的參數(shù),我們在JSP源文件頭加入下面一句:
          ??? <%request.setCharacterEncoding("GB2312");%>
          ??? 3、為了讓JSP編譯器能正確地解碼我們的含有中文字符的JSP文件,我們需要在JSP源文件中指定我們的JSP源文件的編碼格式,具體來說,我們在JSP源文件頭上加入下面的一句即可:
          ??? <
          %@page pageEncoding="GB2312"%>或< %@page pageEncoding="GBK"%>
          ??? 這是JSP規(guī)范2.0新增加的指令。
          ??? 我們建議使用此方法來解JSP文件中的中文問題,下面的代碼是一個正確做法的JSP文件的測試程序:

          //testchinese.jsp
          ??? <%@page pageEncoding="GB2312"%>
          ??? <%@page contentType="text/html; charset=gb2312"%>
          ??? <%request.setCharacterEncoding("GB2312");%>
          ??? <%
          ??? String action = request.getParameter("ACTION");
          ??? String name = "";
          ??? String str = "";
          ??? if(action!=null && action.equals("SENT"))
          ??? {
          ??? name = request.getParameter("name");
          ??? str = request.getParameter("str");
          ??? }
          ??? %>
          ??? <html>
          ??? <head>
          ??? <title></title>
          ??? <Script language="JavaScript">
          ??? function Submit()
          ??? {
          ??? document.base.action = "?ACTION=SENT&str=傳入的中文";
          ??? document.base.method = "POST";
          ??? document.base.submit();
          ??? }
          ??? </Script>
          ??? </head>
          ??? <body bgcolor="#FFFFFF" text="#000000" topmargin="5">
          ??? <form name="base" method = "POST" target="_self">
          ??? <input type="text" name="name" value="" size="30">
          ??? <a href = "JavaScript:Submit()">提交</a>
          ??? </form>
          ??? <%
          ??? if(action!=null && action.equals("SENT"))
          ??? {
          ??? out.println("<br>你輸入的字符為:"+name);
          ??? out.println("<br>你通過URL傳入的字符為:"+str);
          ??? }
          ??? %>
          ??? </body>
          ??? </html>
          ??? 如圖7是此程序運行的結(jié)果示意圖:

          ??? 圖7

          ??? 5、總結(jié)

          ??? 在上面的詳細分析中,我們清晰地給出了JAVA在處理源程序過程中的詳細轉(zhuǎn)換過程,為我們正確解決JAVA編程中的中文問題提供了基礎。同時,我們給出了認為是最優(yōu)的解決JAVA中文問題的辦法。

          我的補充(需要特別注意): 在表單(form )提交時,如果提交的方法為get,那么request.setCharacterEncoding() 是不起作用的。此時在服務器端得到的字符編碼仍然是ISO8859-1,而不是你設置的編碼。所以一般我們在提交數(shù)據(jù)時,盡量使用post方法,此時 request.setCharacterEncoding()方法起效。

          ?如果非要使用get方法傳form則要轉(zhuǎn)換一下才行:?
          ? ----- ?
          ? <%@ ? page ? contentType="text/html;charset=gb2312"%> ?
          ? <%! ?
          ? ? ? ? ? public ? String ? getStr(String ? str){ ?
          ? try{ ?
          ? String ? temp_p=str; ?
          ? byte[] ? temp_t=temp_p.getBytes("ISO8859-1"); ?
          ? String ? temp=new ? String(temp_t); ?
          ? return ? temp; ?
          ? } ?
          ? catch(Exception ? e){ ?
          ? } ?
          ? return ? "null"; ?
          ? ? } ?
          ? ? %> ?
          ? 然后把String ? userId=request.getParameter("userId");改成 ?
          ? String ? userId=getStr(request.getParameter("userId"));??
          --------------------------------------------
          我來說一下tomcat如何實現(xiàn)JSP的你就明白了。
          預備知識:
           1.字節(jié)和unicode
            Java內(nèi)核是unicode的,就連class文件也是,但是很多媒體,包括文件/流的保存方式
            是使用字節(jié)流的。 因此Java要對這些字節(jié)流經(jīng)行轉(zhuǎn)化。char是unicode的,而byte是字節(jié).
            Java中byte/char互轉(zhuǎn)的函數(shù)在sun.io的包中間有。其中ByteToCharConverter類是中調(diào)度,
            可以用來告訴你,你用的Convertor。其中兩個很常用的靜態(tài)函數(shù)是
             public static ByteToCharConverter getDefault() ;
             public static ByteToCharConverter getConverter(String encoding);
            如果你不指定converter,則系統(tǒng)會自動使用當前的Encoding,GB平臺上用GBK,EN平臺上用
            8859_1
            
            我們來就一個簡單的例子:
               "你"的gb碼是:0xC4E3 ,unicode是0x4F60
               你用:
               --encoding="gb2312";
               --byte b[]={(byte)'\u00c4',(byte)'\u00E3'};
               --convertor=ByteToCharConverter.getConverter(encoding);
               --char [] c=converter.convertAll(b);
               --for(int i=0;i<c.length;c++)
               --{
               -- System.out.println(Integer.toHexString(c[i]));
               --}
               --打印出來是0x4F60
               --但是如果使用8859_1的編碼,打印出來是
               --0x00C4,0x00E3
               ----例1
               反過來:
              ? --encoding="gb2312";
              ? --char c[]={'\u4F60'};
               --convertor=ByteToCharConverter.getConverter(encoding);
               --byte [] b=converter.convertAll(c);
               --for(int i=0;i<b.length;c++)
               --{
               -- System.out.println(Integer.toHexString(b[i]));
               --}
                --打印出來是:0xC4,0xE3
                ----例2
                --如果用8859_1就是0x3F,?號,表示無法轉(zhuǎn)化      --
                很多中文問題就是從這兩個最簡單的類派生出來的。而卻有很多類  
            不直接支持把Encoding輸入,這給我們帶來諸多不便。很多程序難得用encoding
            了,直接用default的encoding,這就給我們移植帶來了很多困難
            --
            2.UTF-8
            --UTF-8是和Unicode一一對應的,其實現(xiàn)很簡單
            --
            -- 7位的Unicode: 0 _ _ _ _ _ _ _
            --11位的Unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _
            --16位的Unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
            --21位的Unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
            --大多數(shù)情況是只使用到16位以下的Unicode:
            --"你"的gb碼是:0xC4E3 ,unicode是0x4F60
            --我們還是用上面的例子
            --  --例1:0xC4E3的二進制:
            --  --    1 1 0 0 0 1 0 0 1 1 1 0 0 0 1 1
            --  --    由于只有兩位我們按照兩位的編碼來排,但是我們發(fā)現(xiàn)這行不通,
            --  --    因為第7位不是0因此,返回"?"
            --  --   
            --  --例2:0x4F60的二進制:
            --  --    0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0
            --  --    我們用UTF-8補齊,變成:
            --  --    11100100 10111101 10100000
            --  --    E4--BD-- A0
            --  --    于是返回0xE4,0xBD,0xA0
            --  --
            3.String和byte[]
            --String其實核心是char[],然而要把byte轉(zhuǎn)化成String,必須經(jīng)過編碼。
            --String.length()其實就是char數(shù)組的長度,如果使用不同的編碼,很可
            --能會錯分,造成散字和亂碼。
            --例:
            ----byte [] b={(byte)'\u00c4',(byte)'\u00e3'};
            ----String str=new String(b,encoding);  ----
            ----如果encoding=8859_1,會有兩個字,但是encoding=gb2312只有一個字  ----
            --這個問題在處理分頁是經(jīng)常發(fā)生
            4.Reader,Writer/InputStream,OutputStream
            --Reader和Writer核心是char,InputStream和OutputStream核心是byte。
            --但是Reader和Writer的主要目的是要把Char讀/寫InputStream/OutputStream
          --一個reader的例子:
          --文件test.txt只有一個"你"字,0xC4,0xE3--
          --String encoding=;
          --InputStreamReader reader=new InputStreamReader(
          ----new FileInputStream("text.txt"),encoding);
          --char []c=new char[10];
          --int length=reader.read(c);
          --for(int i=0;i<c.length;i++)
          ----System.out.println(c[i]);
            --如果encoding是gb2312,則只有一個字符,如果encoding=8859_1,則有兩個字符
            --------
          --
          --
            
             ----
           2.我們要對Java的編譯器有所了解:
           --javac -encoding
            我們常常沒有用到ENCODING這個參數(shù)。其實Encoding這個參數(shù)對于跨平臺的操作是很重要的。
            如果沒有指定Encoding,則按照系統(tǒng)的默認Encoding,gb平臺上是gb2312,英文平臺上是ISO8859_1?!?
           --Java的編譯器實際上是調(diào)用sun.tools.javac.Main的類,對文件進行編譯,這個類 --
           有compile函數(shù)中間有一個encoding的變量,-encoding的參數(shù)其實直接傳給encoding變量。
           編譯器就是根據(jù)這個變量來讀取java文件的,然后把用UTF-8形式編譯成class文件。
           一個例子:
           --public void test()
           --{
           ----String str="你";
           ----FileWriter write=new FileWriter("test.txt");
           ----write.write(str);
           ----write.close();
           --}
           ----例3
          --如果用gb2312編譯,你會找到E4 BD A0的字段
          --
          --如果用8859_1編譯,
          --00C4 00E3的二進制:
          --00000000 11000100 00000000 11100011--
          --因為每個字符都大于7位,因此用11位編碼:
          --11000001 10000100 11000011 10100011
          --C1-- 84-- C3--  A3
          --你會找到C1 84 C3 A3 --
              
            但是我們往往忽略掉這個參數(shù),因此這樣往往會有跨平臺的問題:
            --  例3在中文平臺上編譯,生成ZhClass
            --  例3在英文平臺上編譯,輸出EnClass
            --1.  ZhClass在中文平臺上執(zhí)行OK,但是在英文平臺上不行
            --2.  EnClass在英文平臺上執(zhí)行OK,但是在中文平臺上不行
            原因:
           --1.在中文平臺上編譯后,其實str在運行態(tài)的char[]是0x4F60, ----
           --在中文平臺上運行,F(xiàn)ileWriter的缺省編碼是gb2312,因此
           --CharToByteConverter會自動用調(diào)用gb2312的converter,把str轉(zhuǎn)化
           --成byte輸入到FileOutputStream中,于是0xC4,0xE3放進了文件。
           --但是如果是在英文平臺下,CharToByteConverter的缺省值是8859_1,
           --FileWriter會自動調(diào)用8859_1去轉(zhuǎn)化str,但是他無法解釋,因此他會
           --輸出"?" ----
           --2. 在英文平臺上編譯后,其實str在運行態(tài)的char[]是0x00C4 0x00E3, ----
           --在中文平臺上運行,中文無法識別,因此會出現(xiàn)??
           --  在英文平臺上,0x00C4-->0xC4,0x00E3->0xE3,因此0xC4,0xE3被放進了
           --文件
          ----
          1.對于JSP正文的解釋:
          --Tomcat首先看一下你的葉面中有沒有"<
          %@page include的符號。有,則在相同
          --地方設定response.setContentType(..);按照encoding的來讀,沒有他按照8859_1
          --讀取文件,然后用UTF-8寫成.java文件,然后用sun.tools.Main去讀取這個文件,
          --(當然它使用UTF-8去讀),然后編譯成class文件
          --setContentType改變的是out的屬性,out變量缺省的encoding是8859_1
          2.對Parameter的解釋
          --很不幸Parameter只有ISO8859_1的解釋,這個質(zhì)料可以在servlet的實現(xiàn)代碼中找到。
          3.對include的解釋
          格式的,但是很不幸,由于那個寫"org.apache.jasper.compiler.Parser"的人
          在數(shù)組JspUtil.ValidAttribute[]忘記加了一個參數(shù):encoding,因此導致不支
          持這種方式。你完全可以編譯源代碼,加上對encoding的支持
          總結(jié):
          如果你在NT底下,最簡單的方法就是欺騙java,不加任何Encoding變量:
          <html>
          你好<%=request.getParameter("value")%>
          </html>
          http://localhost/test/test.jsp?value =你
          結(jié)果:你好你
          但這種方法局限性較大,比如對上傳的文章分段,這樣的做法是死定的,最好的
          解決方案是用這種方案:
          <%@ page contentType="text/html;charset=gb2312" %>
          <html>
          你好<%=new String(request.getParameter("value").getBytes("8859_1"),"gb2312")%>
          </html>

          <select name="account.accountId" >
          ? ??<OPTION value="<%=account.getAccountId()%>">我的日志</OPTION>
          ? ??<OPTION value="">所有日志</OPTION>
          ? ??<OPTION <%=s%> value="<%=a.getAccountId()%>"><%=a.getAccountName()%></OPTION>
          ? ?</select>
          posted on 2008-01-14 10:25 風之殤 閱讀(405) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 汤原县| 宿州市| 霍邱县| 富顺县| 岳普湖县| 甘谷县| 烟台市| 舒城县| 朝阳市| 洛宁县| 大邑县| 怀柔区| 慈溪市| 柘荣县| 通山县| 鲁甸县| 安仁县| 白玉县| 普宁市| 繁昌县| 陈巴尔虎旗| 余干县| 婺源县| 西城区| 万山特区| 汝州市| 新疆| 涿鹿县| 大埔区| 青川县| 浦江县| 陇川县| 垣曲县| 犍为县| 曲阳县| 泉州市| 琼海市| 和政县| 张家口市| 鹤壁市| 永平县|