hengheng123456789

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            297 Posts :: 68 Stories :: 144 Comments :: 0 Trackbacks
           

          Java 編程技術中漢字問題的分析及解決

          現在 Java 編程語言已經廣泛應用于互聯網世界,早在 Sun 公司開發 Java 語言的時候,就已經考慮到對非英文字符的支持了。Sun 公司公布的 Java 運行環境(JRE)本身就分英文版和國際版,但只有國際版才支持非英文字符。不過在 Java 編程語言的應用中,對中文字符的支持并非如同 Java Soft 的標準規范中所宣稱的那樣完美,因為中文字符集不只一個,而且不同的操作系統對中文字符的支持也不盡相同,所以會有許多和漢字編碼處理有關的問題在我們進行應用開發中困擾著我們。有很多關于這些問題的解答,但都比較瑣碎,并不能夠滿足大家迫切解決問題的愿望,關于 Java 中文問題的系統研究并不多,本文從漢字編碼常識出發,分析 Java 中文問題,希望對大家解決這個問題有所幫助。

          漢字編碼的常識

          我們知道,英文字符一般是以一個字節來表示的,最常用的編碼方法是 ASCII 。但一個字節最多只能區分256個字符,而漢字成千上萬,所以現在都以雙字節來表示漢字,為了能夠與英文字符分開,每個字節的最高位一定為1,這樣雙字節最多可以表示64K格字符。我們經常碰到的編碼方式有 GB2312BIG5UNICODE 等。關于具體編碼方式的詳細資料,有興趣的讀者可以查閱相關資料。我膚淺談一下和我們關系密切的 GB2312 UNICODEGB2312 碼,中華人民共和國國家標準漢字信息交換用編碼,是一個由中華人民共和國國家標準總局發布的關于簡化漢字的編碼,通行于中國大陸地區及新加坡,簡稱國標碼。兩個字節中,第一個字節(高字節)的值為區號值加3220H),第二個字節(低字節)的值為位號值加3220H),用這兩個值來表示一個漢字的編碼。UNICODE 碼是微軟提出的解決多國字符問題的多字節等長編碼,它對英文字符采取前面加“0”字節的策略實現等長兼容。如 A ASCII 碼為0x41UNICODE 就為0x000x41。利用特殊的工具各種編碼之間可以互相轉換。

          Java 中文問題的初步認識

          我們基于 Java 編程語言進行應用開發時,不可避免地要處理中文。Java 編程語言默認的編碼方式是 UNICODE,而我們通常使用的數據庫及文件都是基于 GB2312 編碼的,我們經常碰到這樣的情況:瀏覽基于 JSP 技術的網站看到的是亂碼,文件打開后看到的也是亂碼,被 Java 修改過的數據庫的內容在別的場合應用時無法繼續正確地提供信息。

          String sEnglish = “apple”;

          String sChinese = “蘋果”;

          String s = “蘋果 apple ;

          sEnglish 的長度是5sChinese的長度是4,而 s 默認的長度是14。對于 sEnglish來說, Java 中的各個類都支持得非常好,肯定能夠正確顯示。但對于 sChinese s 來說,雖然 Java Soft 聲明 Java 的基本類已經考慮到對多國字符的支持(默認 UNICODE 編碼),但是如果操作系統的默認編碼不是 UNICODE ,而是國標碼等。從 Java 源代碼到得到正確的結果,要經過 Java 源代碼-> Java 字節碼-> ;虛擬機->操作系統->顯示設備”的過程。在上述過程中的每一步驟,我們都必須正確地處理漢字的編碼,才能夠使最終的顯示結果正確。

          Java 源代碼-> Java 字節碼”,標準的 Java 編譯器 javac 使用的字符集是系統默認的字符集,比如在中文 Windows 操作系統上就是 GBK ,而在 Linux 操作系統上就是ISO-8859-1,所以大家會發現在 Linux 操作系統上編譯的類中源文件中的中文字符都出了問題,解決的辦法就是在編譯的時候添加 encoding 參數,這樣才能夠與平臺無關。用法是

          javac ?Cencoding GBK

          Java 字節碼->虛擬機->操作系統”, Java 運行環境 JRE 分英文版和國際版,但只有國際版才支持非英文字符。 Java 開發工具包 JDK 肯定支持多國字符,但并非所有的計算機用戶都安裝了 JDK 。很多操作系統及應用軟件為了能夠更好的支持 Java ,都內嵌了 JRE 的國際版本,為自己支持多國字符提供了方便。

          “操作系統->顯示設備”,對于漢字來說,操作系統必須支持并能夠顯示它。英文操作系統如果不搭配特殊的應用軟件的話,是肯定不能夠顯示中文的。

          還有一個問題,就是在 Java 編程過程中,對中文字符進行正確的編碼轉換。例如,向網頁輸出中文字符串的時候,不論你是用

          out.println(string);還是用

          <%=string%>,都必須作 UNICODE GBK 的轉換,或者手動,或者自動。在 JSP 1.0中,可以定義輸出字符集,從而實現內碼的自動轉換。用法是

          <%@page contentType=”text/html;charset=gb2312” %>

          但是在一些 JSP 版本中并沒有提供對輸出字符集的支持,(例如 JSP 0.92),這就需要手動編碼輸出了,方法非常多。最常用的方法是

          String s1 = request.getParameter(“keyword”);

          String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);

          getBytes 方法用于將中文字符以“ISO-8859-1”編碼方式轉化成字節數組,而“GBK 是目標編碼方式。我們從以ISO-8859-1方式編碼的數據庫中讀出中文字符串 s1 ,經過上述轉換過程,在支持 GBK 字符集的操作系統和應用軟件中就能夠正確顯示中文字符串 s2

          Java 中文問題的表層分析及處理

          背景 

          開發環境 JDK1.15 Vcafe2.0 JPadPro 

          服務器端 NT IIS Sybase System JconnectJDBC 

          客戶端 IE5.0 Pwin98 ?span > 

          .CLASS 文件存放在服務器端,由客戶端的瀏覽器運行 APPLET APPLET 只起調入 FRAME 類等主程序的作用。界面包括 Textfield TextAreaListChoice 等。

          I. JDBC 執行 SELECT 語句從服務器端讀取數據(中文)后,將數據用 APPEND 方法加到 TextAreaTA ,不能正確顯示。但加到 List 中時,大部分漢字卻可正確顯示。

          將數據按“ISO-8859-1 編碼方式轉化為字節數組,再按系統缺省編碼方式 Default Character Encoding 轉化為 STRING ,即可在 TA List 中正確顯示。

          程序段如下:

          dbstr2 = results.getString(1);

          //After reading the result from DB serverconverting it to string.

          dbbyte1 = dbstr2.getBytes(“iso-8859-1”);

          dbstr1 = new String(dbbyte1);

          在轉換字符串時不采用系統默認編碼方式,而直接采用“ GBK 或者 GB2312 , A B 兩種情況下,從數據庫取數據都沒有問題。

          II.處理方式與“取中文”相逆,先將 SQL 語句按系統缺省編碼方式轉化為字節數組,再按“ISO-8859-1”編碼方式轉化為 STRING ,最后送去執行,則中文信息可正確寫入數據庫。

          程序段如下:

          sqlstmt = tf_input.getText();

          //Before sending statement to DB serverconverting it to sql statement.

          dbbyte1 = sqlstmt.getBytes();

          sqlstmt = newString(dbbyte1,”iso-8859-1”);

          _stmt = _con.createStatement();

          _stmt.executeUpdate(sqlstmt);

          ……

          問題:如果客戶機上存在 CLASSPATH 指向 JDK CLASSES.ZIP 時(稱為 A 情況),上述程序代碼可正確執行。但是如果客戶機只有瀏覽器,而沒有 JDK CLASSPATH 時(稱為 B 情況),則漢字無法正確轉換。

          我們的分析:

          1.經過測試,在 A 情況下,程序運行時系統的缺省編碼方式為 GBK 或者 GB2312 。在 B 情況下,程序啟動時瀏覽器的 JAVA 控制臺中出現如下錯誤信息:

          Can't find resource for sun.awt.windows.awtLocalization_zh_CN

          然后系統的缺省編碼方式為“8859-1”。

          2.如果在轉換字符串時不采用系統缺省編碼方式,而是直接采用 GBK 或“GB2312”,則在 A 情況下程序仍然可正常運行,在 B 情況下,系統出現錯誤:

          UnsupportedEncodingException

          3.在客戶機上,把 JDK CLASSES.ZIP 解壓后,放在另一個目錄中, CLASSPATH 只包含該目錄。然后一邊逐步刪除該目錄中的 .CLASS 文件,另一邊運行測試程序,最后發現在一千多個 CLASS 文件中,只有一個是必不可少的,該文件是:

          sun.io.CharToByteDoubleByte.class

          將該文件拷到服務器端和其它的類放在一起,并在程序的開頭 IMPORT 它,在 B 情況下程序仍然無法正常運行。

          4. A 情況下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,則程序運行時測得默認編碼方式為“8859-1”,否則為 GBK GB2312

          如果 JDK 的版本為1.2以上的話,在 B 情況下遇到的問題得到了很好的解決,測試的步驟同上,有興趣的讀者可以嘗試一下。

          Java 中文問題的根源分析及解決

          在簡體中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Java 運行環境的一些基本屬性,類 PoorChinese 可以幫助我們得到這些屬性。

          PoorChinese 的源代碼:

          public class PoorChinese {

          }

          執行 java PoorChinese 后,我們會得到:

          系統變量 file.encoding 的值為 GBK user.language 的值為 zh user.region 的值為 CN ,這些系統變量的值決定了系統默認的編碼方式是 GBK

          在上述系統中,下面的代碼將 GB2312 文件轉換成 Big5 文件,它們能夠幫助我們理解 Java 中漢字編碼的轉化:

          ?

          import java.io.*;

          import java.util.*;

          ?

          public class gb2big5 {

          ?

          static int iCharNum=0;

          ?

          public static void main(String[] args) {

          System.out.println("Input GB2312 file, output Big5 file.");

          if (args.length!=2) {

          System.err.println("Usage: jview gb2big5 gbfile big5file");

          System.exit(1);

          String inputString = readInput(args[0]);

          writeOutput(inputString,args[1]);

          System.out.println("Number of Characters in file: "+iCharNum+".");

          }

          ?

          static void writeOutput(String str, String strOutFile) {

          try {

          FileOutputStream fos = new FileOutputStream(strOutFile);

          Writer out = new OutputStreamWriter(fos, "Big5");

          out.write(str);

          out.close();

          }

          catch (IOException e) {

          e.printStackTrace();

          e.printStackTrace();

          }

          }

          ?

          static String readInput(String strInFile) {

          StringBuffer buffer = new StringBuffer();

          try {

          FileInputStream fis = new FileInputStream(strInFile);

          InputStreamReader isr = new InputStreamReader(fis, "GB2312");

          Reader in = new BufferedReader(isr);

          int ch;

          while ((ch = in.read()) > -1) {

          iCharNum += 1;

          buffer.append((char)ch);

          }

          in.close();

          return buffer.toString();

          }

          catch (IOException e) {

          e.printStackTrace();

          return null;

          }

          }

          }

          ?

                                

          編碼轉化的過程如下:

          GB2312------------------>Unicode------------->Big5

          執行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的內容是“今天星期三”,則得到的文件 big5.txt 中的字符能夠正確顯示;而如果 gb.txt 的內容是“情人節快樂”,則得到的文件 big5.txt 中對應于“節”和“樂”的字符都是符號“?”(0x3F),可見 sun.io.ByteToCharGB2312 sun.io.CharToByteBig5 這兩個基本類并沒有編好。

          正如上例一樣, Java 的基本類也可能存在問題。由于國際化的工作并不是在國內完成的,所以在這些基本類發布之前,沒有經過嚴格的測試,所以對中文字符的支持并不像 Java Soft 所聲稱的那樣完美。前不久,我的一位技術上的朋友發信給我說,他終于找到了 Java Servlet 中文問題的根源。兩周以來,他一直為 Java Servlet 的中文問題所困擾,因為每面對一個含有中文字符的字符串都必須進行強制轉換才能夠得到正確的結果(這好象是大家公認的唯一的解決辦法)。后來,他確實不想如此繼續安分下去了,因為這樣的事情確實不應該是高級程序員所要做的工作,他就找出 Servlet 解碼的源代碼進行分析,因為他懷疑問題就出在解碼這部分。經過四個小時的奮斗,他終于找到了問題的根源所在。原來他的懷疑是正確的, Servlet 的解碼部分完全沒有考慮雙字節,直接把 %XX 當作一個字符。(原來 Java Soft 也會犯這幺低級的錯誤!)

          如果你對這個問題有興趣或者遇到了同樣的煩惱的話,你可以按照他的步驟 Servlet.jar 進行修改:

          找到源代碼 HttpUtils 中的 static private String parseName ,在返回前將 sbStringBuffer 復制成 byte bs[] ,然后 return new String(bs,GB2312)。作上述修改后就需要自己解碼了:

          HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者

          form=HttpUtils.parsePostData(……)

          千萬別忘了編譯后放到 Servlet.jar 里面。

          關于 Java 中文問題的總結

          Java 編程語言成長于網絡世界,這就要求 Java 對多國字符有很好的支持。 Java 編程語言適應了計算的網絡化的需求,為它能夠在網絡世界迅速成長奠定了堅實的基礎。 Java 的締造者 Java Soft 已經考慮到 Java 編程語言對多國字符的支持,只是現在的解決方案有很多缺陷在里面,需要我們付諸一些補償性的措施。而世界標準化組織也在努力把人類所有的文字統一在一種編碼之中,其中一種方案是 ISO10646 ,它用四個字節來表示一個字符。當然,在這種方案未被采用之前,還是希望 Java Soft 能夠嚴格地測試它的產品,為用戶帶來更多的方便。

          附一個用于從數據庫和網絡中取出 中文亂碼的處理函數,入參是有問題的字符串,出參是問題已經解決了的字符串。

          技術應用者提供解決方案

          關于作者

           段明輝 has co-authored this article

          編譯代碼文件,要用

                   javac -encoding GBK   xxxxxx.java

             如果是用ant工具,

                 <target name="compile" depends="prepare" description="complie All Java source">
                    <copy todir="${build.dir}">
                      <fileset dir="${src.dir}" includes="**/*.properties" />
                   </copy>
                   <javac srcdir="${src.dir}" destdir="${build.dir}" encoding="GBK">
                      <classpath refid="project.classpath" />
                   </javac>

                </target>

          posted on 2007-09-03 15:49 哼哼 閱讀(364) 評論(0)  編輯  收藏 所屬分類: JAVA-Common
          主站蜘蛛池模板: 个旧市| 柞水县| 修武县| 闻喜县| 冕宁县| 株洲县| 金堂县| 修武县| 温宿县| 卓尼县| 临泽县| 屏南县| 体育| 广南县| 双城市| 乐安县| 隆安县| 拜城县| 疏勒县| 福清市| 汶上县| 文登市| 陆河县| 永州市| 龙南县| 乐清市| 松原市| 陇南市| 郴州市| 广饶县| 兴安盟| 余庆县| 丽水市| 蓝田县| 砀山县| 合江县| 平山县| 竹溪县| 林芝县| 奉贤区| 洛阳市|