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