聲明:本文轉載自:http://www.mybole.com.cn/article/815.html
在輸出字符和字符串的時候,會從Unicode編碼向中文系統默認的編碼GBK轉換,由于Unicode編碼0xfffd在GBK字符集中沒有對應的編碼,于是得到0x3f,輸出字符“?”。最后輸出的結果如下:
fffd--?
40--@
554a--啊
從上述所知,由于存在著多種不同的字符集,在各種字符集之間進行轉換,就有可能出現亂碼,同樣是中文字符集GB2312和GBK,由于編碼范圍的不同,某些字符在轉換時也會出現亂碼。
在一個使用了數據庫的Web應用程序中,亂碼可能會在多個環節產生。由于瀏覽器會根據本地系統默認的字符集來提交數據,而Web容器默認采用的是ISO-8859-1的編碼方式解析POST數據,在瀏覽器提交中文數據后,Web容器會按照ISO-8859-1字符集來解碼數據,在這一環節可能會導致亂碼的產生。由于大多數數據庫的JDBC驅動程序默認采用ISO-8859-1的編碼方式在Java程序和數據庫之間傳遞數據,我們的程序在向數據庫中存儲包含中文的數據時,JDBC驅動首先將程序內部的Unicode編碼格式的數據轉化為ISO-8859-1的格式,然后傳遞到數據庫中,在這一環節可能會導致亂碼的產生。目前流行的關系型數據庫系統都支持數據庫編碼,也就是說在創建數據庫時可以指定它自己的字符集設置,數據庫的數據以指定的編碼形式存儲。當JDBC驅動向數據庫中保存數據時,有可能還會發生字符集的轉換。正是由于在Web應用程序運行過程中,輸入的中文字符需要在不同的字符集之間來回轉換,也就導致了中文亂碼問題的頻繁出現。
圖17-1 描述了在Web應用的請求響應過程中,發生的字符編碼轉換過程,其中瀏覽器是IE 6.0,Web容器的是Tomcat 6.0.16。
從圖17-1 描述的過程中可以看到,如果在Web應用程序中不指定任何的字符集,從瀏覽器端傳來的中文字符,輸出回瀏覽器時,可以正常顯示(以簡體中文的方式查看網頁)。然而,事情并沒有這么簡單,在Servlet/JSP中,可能存在著直接寫入的或從其他來源讀取的中文字符,如果這些字符對應的Unicode碼是從GB2312編碼轉換而來,那么以ISO-8859-1編碼方式輸出,這些字符將不能正常顯示。所以對于中文的處理,應該在圖17-1②和⑤的位置明確指定使用GB2312或GBK字符集。
圖17-1 在Web請求響應過程中,中文字符編碼的轉換過程
java中文亂碼問題的解決方案
只要掌握了中文亂碼問題產生的原因,然后對癥下藥,就可以順利地解決這些問題。下面我們對容易產生亂碼問題的場景進行分析,并提出解決方案。
1.以POST方法提交的表單數據中有中文字符
由于Web容器默認的編碼方式是ISO-8859-1,在Servlet/JSP程序中,通過請求對象的getParameter()方法得到的字符串是以ISO-8859-1轉換而來,這是導致亂碼產生的原因之一。為了避免容器以ISO-8859-1的編碼方式返回字符串,對于以POST方法提交的表單數據,可以在獲取請求參數值之前,調用request.setCharacterEncoding("GBK"),明確指定請求正文使用的字符編碼方式是GBK。在向瀏覽器發送中文數據之前,調用response.setContentType("text/html;charset=GBK"),指定輸出內容的編碼方式是GBK。
對于JSP頁面,在獲取請求參數值之前,寫上下面的代碼:
<%request.setCharacterEncoding("GB2312");%>
為了指定輸出內容的編碼格式,設置page指令contentType屬性,如下:
<%@ page contentType="text/html; charset=GBK"
%>
在Web容器轉換JSP頁面后的Servlet類中,會自動添加下面的代碼:
response.setContentType("text/html;
charset=GBK");
2.以GET方法提交的表單數據中有中文字符
當提交表單采用GET方法時,提交的數據作為查詢字符串被附加到URL的末端,發送到服務器,此時在服務器端調用setCharacterEncoding()方法也就沒有作用了。我們需要在得到請求參數的值后,自己做正確的編碼轉換。
String name = request.getParameter("name");
name=new
String(name.getBytes("ISO-8859-1"),"GBK");
在第一行,調用getParameter()方法得到的字符串name的Unicode值是以ISO-8859-1編碼轉換而來,調用name.getBytes("ISO-8859-1"),將得到原始的GBK編碼值,接著,對new String()的調用將以GBK字符集重新構造字符串的Unicode編碼。
為了方便從ISO-8859-1編碼到GBK的轉換,我們可以編寫一個工具方法,如下:
public String toGBK(String str)
throws java.io.UnsupportedEncodingException
{
return new
String(str.getBytes("ISO-8859-1"),"GBK");
}
3.在數據庫中存儲和讀取中文數據
對于大多數數據庫的JDBC驅動程序,在Java程序和數據庫之間傳遞數據都是以ISO-8859-1為默認編碼格式,所以,我們在程序中向數據庫存儲包含中文的數據時,JDBC驅動程序首先把程序內部的Unicode編碼格式的數據轉化為ISO-8859-1編碼,然后傳遞到數據庫中,加上數據庫本身也有字符集,這就是為什么我們常常在數據庫中讀取中文數據時,讀到的是亂碼。
要解決上述問題,只需要將數據庫默認的編碼格式改為GBK或GB2312即可,不同的數據庫還提供了另外的方式來處理字符編碼轉換的問題,讀者在實際應用過程中,可針對具體情況再做具體處理,只要理解了編碼轉換的過程,就能找到問題的所在,進而解決問題。4.Servlet/JSP在不同語言系統的平臺下運行
有時候,我們在中文系統平臺下開發的Web應用程序移植到英文系統平臺下,在Servlet和JSP中直接書寫的中文字符串在輸出時,將顯示為亂碼。這是因為在編譯Servlet類或者JSP文件時,如果沒有使用-encoding參數指定Java源程序的編碼格式,javac會獲取本地操作系統默認采用的字符集,以該字符集將Java源程序轉換為Unicode編碼保存到內存中,然后將源程序編譯為字節碼文件(字節碼文件采用的是UTF-8編碼),保存到硬盤上。
在英文平臺下,采用的默認編碼格式是ISO-8859-1,所以在編譯轉換后,執行輸出時,原先在源文件中書寫的中文字符串就變成了亂碼。
要解決這個問題,在編譯Servlet類的源程序時,可以用-encoding參數指定編碼為GBK或GB2312,例如:
javac –encoding GBK HelloServlet.java
對于JSP頁面,只要在page指令中用contentType屬性或pageEncoding屬性指定編碼格式為GBK或GB2312,Web容器就可以正確轉換和編譯JSP文件了。例如:
<%@ page contentType="text/html;
charset=GBK" %>
或
<%@ page pageEncoding="GBK" %>
在實際的Web應用中,亂碼問題產生的原因多種多樣,然而只要我們理解了字符編碼的轉換過程,仔細地分析亂碼產生的原因,找到問題的關鍵,就能對癥下藥,解決問題。