JSP中文亂碼處理
一、Java中文問題的由來
Java的核心和class檔案是基於unicode的,這使Java程式具有良好的跨平臺性,但也帶來了一些中文亂碼問題的麻煩。原因主要有兩方面,Java和JSP檔案本身編譯時產生的亂碼問題和Java程式於其他媒介互動產生的亂碼問題。
首先Java(包括JSP)原始檔中很可能包含有中文,而Java和JSP原始檔的儲存方式是基於位元組流的,如果Java和JSP編譯成class檔案過程中,使用的編碼方式與原始檔的編碼不一致,就會出現亂碼。基於這種亂碼,建議在Java檔案中儘量不要寫中文(註釋部分不參與編譯,寫中文沒關係),如果必須寫的話,儘量手動帶引數-ecoding GBK或-ecoding gb2312編譯;對於JSP,在檔案頭加上或基本上就能解決這類亂碼問題。
本文要重點討論的是第二類亂碼,即Java程式與其他儲存媒介互動時產生的亂碼。很多儲存媒介,如資料庫,檔案,流等的儲存方式都是基於位元組流的,Java程式與這些媒介互動時就會發生字元(char)與位元組(byte)之間的轉換,具體情況如下:
從頁面form提交資料到java程式 byte->char
從java程式到頁面顯示 char?>byte
從資料庫到java程式 byte?>char
從java程式到資料庫 char?>byte
從檔案到java程式 byte->char
從java程式到檔案 char->byte
從流到java程式 byte->char
從java程式到流 char->byte
如果在以上轉換過程中使用的編碼方式與位元組原有的編碼不一致,很可能就會出現亂碼。
二、解決方法
前面已經提到了Java程式與其他媒介互動時字元和位元組的轉換過程,如果這些轉換過程中容易產生亂碼。解決這些亂碼問題的關鍵在於確保轉換時使用的編碼方式與位元組原有的編碼方式保持一致,下面分別論述(Java或JSP自身產生的亂碼請參看第一部分)。
1、JSP與頁面引數之間的亂碼
JSP獲取頁面引數時一般採用系統預設的編碼方式,如果頁面引數的編碼型別和系統預設的編碼型別不一致,很可能就會出現亂碼。解決這類亂碼問題的基本方法是在頁面獲取引數之前,強制指定request獲取引數的編碼方式:request.setCharacterEncoding("GBK")或request.setCharacterEncoding("gb2312")。
如果在JSP將變數輸出到頁面時出現了亂碼,可以通過設定response.setContentType("text/html;charset=GBK")或response.setContentType("text/html;charset=gb2312")解決。
如果不想在每個檔案裡都寫這樣兩句話,更簡潔的辦法是使用Servlet規範中的過慮器指定編碼,過濾器的在web.xml中的典型配置和主要程式碼如下:
[color=red]適用於 post[/color]
1.拷貝tomcat下面 filters
SetCharacterEncodingFilter.java到SRC下面(或再建個包filters)
2.修改web.xml
//亂碼過濾器
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
[color=red]適用於GET[/color]
修改tomcat conf下的server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
下面加一行
URIEncoding="GBK2312"
呼叫轉換函式
<%!
public String getStr(String s){
String str=s;
try{
byte[] b=str.getBytes("ISO-8859-1");
str=new String(b);
return str;
}catch(Exception e){
return str;
}
}
%>
2、Java與資料庫之間的亂碼
大部分資料庫都支援以unicode編碼方式,所以解決Java與資料庫之間的亂碼問題比較明智的方式是直接使用unicode編碼與資料庫互動。很多資料庫驅動自動支援unicode,如Microsoft的SQLServer驅動。其他大部分資料庫驅動,可以在驅動的url引數中指定,如如mm的mysql驅動:jdbc:mysql://localhost/WEBCLDB?useUnicode=true&characterEncoding=GBK。
3、Java與檔案/流之間的亂碼
Java讀寫檔案最常用的類是FileInputStream/FileOutputStream和FileReader/FileWriter。其中FileInputStream和FileOutputStream是基於位元組流的,常用於讀寫二進位制檔案。讀寫字元檔案建議使用基於字元的FileReader和FileWriter,省去了位元組與字元之間的轉換。但這兩個類的建構函式預設使用系統的編碼方式,如果檔案內容與系統編碼方式不一致,可能會出現亂碼。在這種情況下,建議使用FileReader和FileWriter的父類:InputStreamReader/OutputStreamWriter,它們也是基於字元的,但在建構函式中可以指定編碼型別:InputStreamReader(InputStream in, Charset cs) 和OutputStreamWriter(OutputStream out, Charset cs)。
4、其他
上面提到的方法應該能解決大部分亂碼問題,如果在其他地方還出現亂碼,可能需要手動修改程式碼。解決Java亂碼問題的關鍵在於在位元組與字元的轉換過程中,你必須知道原來位元組或轉換後的位元組的編碼方式,轉換時採用的編碼必須與這個編碼方式保持一致。我們以前使用Resin伺服器,使用smartUpload元件上傳檔案,上傳檔案同時傳遞的中文引數獲取沒有亂碼問題。當在Linux中把Resin設定成服務後,上傳檔案同時的中文引數獲取出現了亂碼。這個問題困擾了我們很久,後來我們分析smartUpload元件的原始檔,因為檔案上傳採用的是位元組流的方式,裡面包含的引數名稱和值也是位元組流的方式傳遞的。smartUpload元件讀取位元組流後再將引數名稱和值從位元組流中解析出來,問題就出現在smartUpload將位元組流轉換成字串時採用了系統預設的編碼,而將Resin設定成服務後,系統預設的編碼可能發生了改變,因此出現了亂碼。後來,我們更改了smartUpload的原始檔,增加了一個屬性charset和setCharset(String)方法,將upload()方法中提取引數語句:
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1 );
改成了
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1, charset );
終於解決了這個亂碼問題