中文編碼問題解決方法彙總

常見的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來處理的。 

4、中文問題的分類及其建議最優解決辦法 

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

具體解決辦法如下: 

1、 針對直接在console上運行的類 
對於這種情況,我們建議在程序編寫時,如果需要從用戶端接收用戶的可能含有中文的輸入或含有中文的輸出,程序中應該採用字符流來處理輸入和輸出,具體來說,應用以下面向字符型節點流類型: 
對文件:FileReader,FileWrieter 
其字節型節點流類型爲:FileInputStream,FileOutputStream 
對內存(數組):CharArrayReader,CharArrayWriter 
其字節型節點流類型爲:ByteArrayInputStream,ByteArrayOutputStream 
對內存(字符串):StringReader,StringWriter 
對管道:PipedReader,PipedWriter 
其字節型節點流類型爲:PipedInputStream,PipedOutputStream 
同時,應該用以下面向字符型處理流來處理輸入和輸出: 
BufferedWriter,BufferedReader 
其字節型的處理流爲:BufferedInputeStream,BufferedOutputStream 
InputStreamReader,OutputStreamWriter 
其字節型的處理流爲:DataInputStream,DataOutputStream 
其中InputStreamReader和InputStreamWriter用於將字節流按照指定的字符編碼集轉換到字符流,如: 
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中文測試,這是內部硬編碼的串"+"/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 

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

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

3、 針對Servlet類 

針對Servlet,我們建議用以下方法: 

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

//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("Hello World! This is created by Servlet!測試中文!"); 


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("你傳入的中文字串是:" + name); 
out.println("你輸入的id是:" + id); 

public void destroy() { } 
請用javac -encoding gb2312 HelloWorld.java來編譯此程序。 


4、 JAVA程序和數據庫之間 

爲避免JAVA程序和數據庫之間數據傳遞出現亂碼現象,我們建議採用以下最優方法來處理: 
1、 對於JAVA程序的處理方法按我們指定的方法處理。 
2、 把數據庫默認支持的編碼格式改爲GBK或GB2312的。 

如:在mysql中,我們可以在配置文件my.ini中加入以下語句實現: 
在[mysqld]區增加: 
default-character-set=gbk 
並增加: 
[client] 
default-character-set=gbk 
在SQL Server2K中,我們可以將數據庫默認的語言設置爲Simplified Chinese來達到目的。 

5、 針對JSP代碼 

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

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

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

2、爲了讓JSP能正確獲得傳入的參數,我們在JSP源文件頭加入下面一句: 

<%request.setCharacterEncoding(”GB2312″);%> 

3、爲了讓JSP編譯器能正確地解碼我們的含有中文字符的JSP文件,我們需要在JSP源文件中指定我們的JSP源文件的編碼格式,具體來說,我們在JSP源文件頭上加入下面的一句即可: 
或 
這是JSP規範2.0新增加的指令。 
我們建議使用此方法來解JSP文件中的中文問題,下面的代碼是一個正確做法的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″> 
提交 
</form> 
<% 
if(action!=null && action.equals(”SENT”)) 

out.println(”<br>你輸入的字符爲:”+name); 
out.println(”
你通過URL傳入的字符爲:”+str); 

%> 
</body> 
</html> 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章