自從接觸Java和JSP以來,就不斷與Java的中文亂碼問題打交道,現在終於得到了徹底的解決,現將我們的解決心得與大家共用。
一、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中的典型配置和主要代碼如下:
web.xml:
CharacterEncodingFilter
net.vschool.web.CharacterEncodingFilter
encodingGBK
CharacterEncodingFilter
/*
CharacterEncodingFilter.java:
public class CharacterEncodingFilter implements Filter
{
protected String encoding = null;
public void init(FilterConfig filterConfig) throws ServletException
{
this.encoding = filterConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
chain.doFilter(request, response);
}
}
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 );
終於解決了這個亂碼問題。
轉自:http://www.enet.com.cn/article/2008/0229/A20080229170410.shtml
--------------add---------------
轉自:http://yongtree.spaces.live.com/blog/cns!9F387A11DF33043D!231.entry
對於初學jsp web開發的人來說,中文亂碼是一個比較頭痛的問題。在學習和做項目的過程中,同樣被這些問題困惱著,當問題出的多了,也有了一定的經驗,現寫下來與朋友們分享。1.JSP輸出中文的亂碼問題
所謂在jsp輸出中文,即直接在jsp中輸出中文,或者給變數賦中文值再輸出等,這種情況下的亂碼問題往往是因為沒有給jsp頁面制定顯示中文字元的編碼方式,解決辦法如下:
1)在jsp頁面頭部加上語句<%@ page contentType="text/html;charset=utf-8"%>(在Servlet中使用httpServletResponse.setContentType("text/html;charset=utf-8"),最好同時在jsp頁面的head部分加上<meta http-equiv="Content-Type" content="text/html;charset="utf-8">
2)在每次要輸出中文的地方主動轉換編碼方式,比如要在頁面中輸入“中文”二字,就可以用以下的方法:
<%
String str="中文";
byte[] tmpbyte=str.getBytes("ISO8859_1");
str=new String(tmpbyte);
out.println(str);
%>
對於以上這兩種方法,顯然第一種方法更通用一點,只需要在一個頁面中添加一次代碼即可;而對於第二種方法,在每個需要輸出中文的地方都需要轉碼,如果這樣的地方很多,這將是一個繁重的工作。
2.擷取表單提交的資料時的中文亂碼問題
在沒有加任何其他處理之前,用request.getParameter("paramName")擷取表單提交中的資料,且表單資料中含有中文時,返回的字串會呈現亂碼。出現這種問題的原因是Tomcat的j2ee實現對錶單提交,即以POST方式提交的參數採用預設的ISO-8859-1來處理。
解決此問題的辦法有兩個:
1)不修改其他配置,只是在將表單中的中文資料區出來後再轉換編碼,方法如語句 String str=request.getParameter("chStr");String str = new String(str.getBytes("ISO-8859-1"),"UTF-8");但這種方法只是從一個局部來考慮問題,如果這樣的情況很多,就要寫很多次,勢必加大工作量。
2)讓對所有頁面的請求都通過一個Filter,將處理字元集設定為utf-8(根據自己需要也可以設定成其他的,如gb2312,gbk)。具體做法參考Tomcat的webapps/servlet-exemples目錄有一個完整的例子,也可以參考其中web.xml和SetCharacterEncodingFilter的配置.
3.URL中的中文問題
對於直接通過在url中傳遞中文參數,如http://localhost:8080/a.jsp?str="中文"這樣的get請求,在伺服器端用request.getParameter("name")時返回的往往是亂碼。按照以上的做法設定Filter沒有用,用request.setCharacterEncoding("utf-8")的方式,仍然不管用。造成這種結果的原因是Tomcat中以get方式提交的請求對query-string處理時採用了和post方法不一樣的處理方式。
解決這個問題的方法是是開啟Tomcat安裝目錄下的/conf/server.xml檔案,找到Connector塊,往其中添加URLEncoding="utf-8"/>
4.資料庫訪問時的亂碼問題
資料庫中所有表的編碼方式和jsp中的使用的編碼要保持一致,這樣做的目的可以減少不必要的編碼轉換問題.另外,在使用jdbc串連MySQL資料庫時,連接字串寫成如下形式可以避免一些中文問題:
jdbc://mysql://hostname:port/DBname?user=username&password=pwd&useUnicode=true&character Encoding=utf-8
如果是以資料來源的方式串連資料庫,設定檔中使用:
<parameter>
<name>url</name>
<value>jdbc:mysql://hostname:port/DBname?&useUnicode=true&characterEncoding=utf-8
</value>
</parameter>
但是如果使用一個已經存在的資料庫,資料庫的編碼方式為ISO-8859-1,而Web應用中使用的utf-8,且資料庫已經有很多重要的資訊,因此不能通過更改資料庫的編碼方式來解決。這個時候,在往資料庫中寫資料時,一定要在jdbc連接字串中加入“useUnicode=true&characterEncoding=ISO-8859-1”,這樣可以順利的王資料庫寫入正常的資料。但是,在將資料讀出資料庫時,亂碼又會出現,這個時候就應該在資料取出時對其轉碼,可以將轉碼功能寫為一個函數,具體實現如下:
public String charConvert(String src){
String result=null;
if(src!=null){
try{
result=new String(src.getBytes("ISO-8859-1"),"UTF-8");
}catch(Exception e){
result=null;
}
}
return result;
}
於是,在資料庫讀出資料過後調用charConvert(rs.getString("colName"));這樣就可以正常的顯示資料庫中的中文資料了。
--------------add---------------
在頁面傳參數時若出現亂碼,可嘗試使用encodeURI方法封裝url。如
encodeURI("<%=request.getContextPath()%>/dyfk/ajax/list.jsp?id="+id+"&isgxpcs="+isgxpcs+"&deptName="+deptName)