我們常見(jiàn)的亂碼大致有如下幾種情形:

l  漢字變成了問(wèn)號(hào)“?”

l  有的漢字顯示正確,有的則顯示錯(cuò)誤

l  顯示亂碼(有些是漢字但并不是你預(yù)期的)

l  讀寫(xiě)數(shù)據(jù)庫(kù)出現(xiàn)亂碼

字符變問(wèn)號(hào)

Java中byte與char相互轉(zhuǎn)換的方法在sun.io包中。其中,byte到char的常用轉(zhuǎn)換方法是:

public static ByteToCharConverter getConverter(String encoding);

為了便于大家理解,我們先來(lái)做一個(gè)小實(shí)驗(yàn):比如,漢字“你”的GBK編碼為0xc4e3,其Unicode編碼是"u4f60。我們的實(shí)驗(yàn)是這樣的,先有一個(gè)頁(yè)面比如名為a_gbk.jsp,在其中輸入漢字“你”,提交給頁(yè)面b_gbk.jsp。在b_gbk.jsp文件中以某種編碼方式得到“你”的字節(jié)數(shù)組,再將該數(shù)組以某種編碼方式轉(zhuǎn)換成char,如果得到的char值是0x4f60則轉(zhuǎn)換是正確的。

a_gbk.jsp的代碼如下:

代碼清單13-1

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>

<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">

  <tr>

    <td>&nbsp;</td>

    <td >&nbsp;</td>

    <td>&nbsp;</td>

  </tr>

  <tr>

    <td width="100">&nbsp;</td>

    <td >Input</td>

    <td width="100">&nbsp;</td>

  </tr>

  <tr>

    <td>&nbsp;</td>

    <td >&nbsp;</td>

    <td>&nbsp;</td>

  </tr>

</table>

<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">

  <tr>

    <td><form method="post" action="b_gbk.jsp">

        <table width="611" border="0" cellpadding="0" cellspacing="0">

          <tr>

            <td width="100" align="right"></td>

            <td><input name="ClsID" type="text"  id="ClsID" maxlength="2" >

              *</td>

          </tr>

          <tr>

            <td width="100" align="right">&nbsp;</td>

            <td><input name="btn" type="submit" value="OK">

             </td>

          </tr>

        </table>

      </form></td>

  </tr>

</table>

b_gbk.jsp的代碼如下:

代碼清單13-2

<%@ page contentType="text/html; charset=GBK" import="sun.io.*" %>

<%

String reqValue=(String)request.getParameter("ClsID");

byte b[]=reqValue.getBytes("ISO8859-1");

out.println("print byte value<br>");

for(int j=0;j<b.length;j++){

  out.println(Integer.toHexString(b[j])+"<br>");

}

ByteToCharConverter convertor=ByteToCharConverter.getConverter("GBK");

char[] c=convertor.convertAll(b);

out.println("byte length:"+b.length+"<br>");

out.println("char length:"+c.length+"<br>");

out.println("print char value<br>");

for(int i=0;i<c.length;i++){

    out.println(Integer.toHexString(c[i])+"<br>");

}

String reqValue1=new String(reqValue.getBytes("ISO8859-1"),"GBK");

%>

<%="reqValue是:"+reqValue%><br>

<%="reqValue1是:"+reqValue1%>

實(shí)驗(yàn)的步驟如下:

文本框:  
圖13.3

先在Tomcat服務(wù)器的子目錄webapps下建立一個(gè)名為charset目錄,當(dāng)然這個(gè)目錄可以任意命名,然后將a_gbk.jsp和b_gbk.jsp文件保存在該目錄下。啟動(dòng)Tomcat服務(wù)器,打開(kāi)IE瀏覽器,在地址欄中輸入http://127.0.0.1:8080/charset/a_gbk.jsp

此時(shí),將出現(xiàn)帶一個(gè)輸入文本和按鈕的頁(yè)面,在文本框中輸入“你”字(不包括雙引號(hào)),點(diǎn)擊OK按鈕,會(huì)得到如圖13.3所示的結(jié)果頁(yè)面。

現(xiàn)在對(duì)該結(jié)果做一些必要的解釋:

結(jié)合代碼清單13-2中可以看出:b_gbk.jsp文件,首先用一個(gè)名為reqValue的字符串變量取得從a_gbk.jsp的文本框的值。然后,用一個(gè)字節(jié)數(shù)組變量取得reqValue的字節(jié)值,并將它們打印在頁(yè)面上,這就是圖13.3中的第二、三行的c4和e3。接著在頁(yè)面上打印字節(jié)長(zhǎng)度,其結(jié)果是2,再又用ByteToCharConverter轉(zhuǎn)換器將字節(jié)轉(zhuǎn)換為字符數(shù)組并打印字符數(shù)組的長(zhǎng)度即字符數(shù)組中的字符個(gè)數(shù),其結(jié)果是1,表示是一個(gè)字符即‘你’。接下來(lái)打印字符的值是4f60,這正是我們前面提到的“你”字的Unicode編碼"u4f60。

這里要注意的是:byte b[]=reqValue.getBytes("ISO8859-1");中的編碼是ISO8859-1,這就是我們前面提到的有些web容器在您沒(méi)有指定request的字符集時(shí)它就采用缺省的ISO8859-1。

從圖中我們還看到表達(dá)式<%="reqValue是:"+reqValue%>中的reqValue并沒(méi)有正確地顯示“你”而是變成“??”這是什么原因呢?這里的reqValue是作為一個(gè)String被顯示的,我們來(lái)看看我們常用的String構(gòu)造函數(shù):

String(byte[] bytes,String encoding);

在國(guó)標(biāo)平臺(tái)上,該函數(shù)會(huì)認(rèn)為bytes是按GBK編碼的,如果后一個(gè)參數(shù)省略,它也會(huì)認(rèn)為是        encoding為GBK。

對(duì)前一個(gè)參數(shù)就相當(dāng)于將b_gbk.jsp文件的這句byte b[]=reqValue.getBytes("ISO8859-1");中的ISO8859-1改為GBK,這樣顯然在GBK字符集中找不到相應(yīng)的目的編碼,它給出的結(jié)果是0x3f、0x3f。因此,就會(huì)顯示為“??”,這也就是造成亂碼的第一種現(xiàn)象的原因。

顯然,造成這種亂碼是由于請(qǐng)求編碼采用默認(rèn)的ISO8859-1編碼所引起的,實(shí)驗(yàn)中的<%="reqValue1是:"+reqValue1%>語(yǔ)句中的reqValue1能正確顯示,給出了這個(gè)問(wèn)題的一種解決方法,即我們平常所說(shuō)的轉(zhuǎn)碼就是采用b_gbk.jsp中語(yǔ)句:

String reqValue1=new String(reqValue.getBytes("ISO8859-1"),"GBK");

文本框:  
圖13.4

這樣的做法,這也是早期中文jsp編程教材和編程實(shí)踐中曾被普遍采用的辦法。這種做法會(huì)使得應(yīng)用程序的代碼中充斥這種轉(zhuǎn)碼語(yǔ)句。其實(shí)存在著比這種做法更好一些的做法,就是在處理請(qǐng)求前,設(shè)置請(qǐng)求字符的編碼:

request.setCharacterEncoding("GBK");

我們繼續(xù)用實(shí)驗(yàn)來(lái)驗(yàn)證這種方法,將request.setCharacter- Encoding("GBK");這一句加到b_gbk.jsp文件中語(yǔ)句String reqValue=(String)request.getParameter("ClsID");的前一行,保存后再做前面的實(shí)驗(yàn),就會(huì)看到圖13.4所示的結(jié)果。

從圖13.4可以看出,原來(lái)不能正確顯示的reqValue可以正確顯示了,而原來(lái)所有正確的東西反而變得不正確了。造成這種結(jié)果的原因就是現(xiàn)在request的值的編碼不再是缺省的ISO8859-1,而是新設(shè)置的GBK。顯而易見(jiàn),要想使它們都正常,只要把b_gbk.jsp文件中的ISO8859-1都改為GBK就可以了。改動(dòng)后的文件如下:

代碼清單13-3

<%@ page contentType="text/html; charset=GBK" import="sun.io.*" %>

<%

request.setCharacterEncoding("GBK");

String reqValue=(String)request.getParameter("ClsID");

byte b[]=reqValue.getBytes("GBK");

out.println("print byte value<br>");

for(int j=0;j<b.length;j++){

  out.println(Integer.toHexString(b[j])+"<br>");

}

ByteToCharConverter convertor=ByteToCharConverter.getConverter("GBK");

char[] c=convertor.convertAll(b);

out.println("byte length:"+b.length+"<br>");

out.println("char length:"+c.length+"<br>");

out.println("print char value<br>");

for(int i=0;i<c.length;i++){

    out.println(Integer.toHexString(c[i])+"<br>");

}

String reqValue1=new String(reqValue.getBytes("GBK"),"GBK");

%>

<%="reqValue是:"+reqValue%><br>

<%="reqValue1是:"+reqValue1%>

清單中的黑體部分是改動(dòng)的部分。

這種方法比起轉(zhuǎn)碼來(lái),前進(jìn)了一步,就是每個(gè)相關(guān)的.jsp文件在處理請(qǐng)求數(shù)據(jù)之前加上一句:request.setCharacterEncoding("GBK");就可以了。但是當(dāng)一個(gè)項(xiàng)目很大時(shí),要在所有相關(guān)的.jsp文件上加上這個(gè)代碼也還是比較麻煩的。更好的辦法是這個(gè)代碼設(shè)置在Servlet的過(guò)濾器(filter)中,語(yǔ)句如下:

ServletRequest.setCharacterEncoding("GBK");

后面會(huì)給出這個(gè)過(guò)濾器的完整示例代碼。

我們的例子是演示的從byte到char的轉(zhuǎn)換過(guò)程,相反的過(guò)程也會(huì)造成同樣的問(wèn)題,大家自己可以做類似的實(shí)驗(yàn)來(lái)驗(yàn)證。

部分漢字是亂碼

造成這個(gè)問(wèn)題的原因是,采用了GB2312的緣故,前面我們已經(jīng)講過(guò),GB2312只包含數(shù)千個(gè)字符。因此,有些不太常用的漢字如人名、地名中比較特別的漢字,在字符集中找不到對(duì)應(yīng)的編碼,故這部分漢字就無(wú)法正常顯示。可以用實(shí)驗(yàn)來(lái)驗(yàn)證這種情況:

將代碼清單13-3中的:

request.setCharacterEncoding("GBK");語(yǔ)句中的GBK改為GB2312再做實(shí)驗(yàn),將會(huì)發(fā)現(xiàn)在a_gbk.jsp中輸入“你”字可以正常顯示,但輸入朱镕基的“镕”字就不能正常顯示。因此,處理中文字符時(shí)不推薦使用GB2312,要么使用GBK,要么使用GB18030。



顯示亂碼(有些是漢字但并不是你預(yù)期的)

在上面,我們采用request.setCharacterEncoding("GBK");的方法已經(jīng)能比較成功地解決一些漢字亂碼問(wèn)題了。很顯然,用request.setCharacterEncoding("GBK");這種特定中文字符集的辦法只能解決中文相關(guān)問(wèn)題。而不能完整地解決I18N編程問(wèn)題。從前面介紹的字符集的基礎(chǔ)可以看出,統(tǒng)一使用UTF-8字符集不失為一種比較有效的方法。為了能比較直觀地說(shuō)明問(wèn)題,我們來(lái)接著做上面的實(shí)驗(yàn)。

將a_gbk.jsp中的GBK都替換為UTF-8,將action=b_gbk.jsp改為action=b.jsp。將文件另存為a.jsp,也放在與a_gbk.jsp相同的目錄下。因?yàn)槲募械腸ontentType="text/html; charset=UTF-8"字符集指定為UTF-8且我們又沒(méi)有指定pageEncoding的值,因此,存文件時(shí)編碼應(yīng)選擇為UTF-8。更改后的文件代碼如下:

代碼清單13-4

<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %>

<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">

  <tr>

    <td>&nbsp;</td>

    <td >&nbsp;</td>

    <td>&nbsp;</td>

  </tr>

  <tr>

    <td width="100">&nbsp;</td>

    <td >Input</td>

    <td width="100">&nbsp;</td>

  </tr>

  <tr>

    <td>&nbsp;</td>

    <td >&nbsp;</td>

    <td>&nbsp;</td>

  </tr>

</table>

<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">

  <tr>

    <td><form method="post" action="b.jsp">

        <table width="611" border="0" cellpadding="0" cellspacing="0">

          <tr>

            <td width="100" align="right"></td>

            <td><input name="ClsID" type="text"  id="ClsID" maxlength="2" >

              *</td>

          </tr>

          <tr>

            <td width="100" align="right">&nbsp;</td>

            <td><input name="btn" type="submit" value="OK">

             </td>

          </tr>

        </table>

      </form></td>

  </tr>

</table>

同樣地,將b_gbk.jsp中的GBK全部替換為UTF-8,另存為b.jsp,放在與b_gbk.jsp相同的目錄下。與前面同樣的道理,保存時(shí)選擇編碼為UTF-8。更改后的代碼如下:

代碼清單13-5

<%@ page contentType="text/html; charset=UTF-8" import="sun.io.*" %>

<%

request.setCharacterEncoding("UTF-8");

String reqValue=(String)request.getParameter("ClsID");

byte b[]=reqValue.getBytes("UTF-8");

out.println("print byte value<br>");

for(int j=0;j<b.length;j++){

  out.println(Integer.toHexString(b[j])+"<br>");

}

ByteToCharConverter convertor=ByteToCharConverter.getConverter("UTF-8");

char[] c=convertor.convertAll(b);

out.println("byte length:"+b.length+"<br>");

out.println("char length:"+c.length+"<br>");

out.println("print char value<br>");

for(int i=0;i<c.length;i++){

    out.println(Integer.toHexString(c[i])+"<br>");

}

String reqValue1=new String(reqValue.getBytes("UTF-8"),"UTF-8");

%>

<%="reqValue是:"+reqValue%><br>

<%="reqValue1是:"+reqValue1%>

文本框:  
圖13.5

在瀏覽器中訪問(wèn)a.jsp,在輸入框中輸入“你”,則結(jié)果頁(yè)面如圖13.5所示。

從圖13.5中可以看出,在UTF-8中一個(gè)漢字是用三個(gè)byte表示的,它們的值分別是0xe4、0xbd、0xa0,也就是說(shuō)用UTF-8來(lái)表示漢字,每個(gè)漢字要比GBK多占用一個(gè)byte,這也是使用UTF-8要多付出的一點(diǎn)代價(jià)吧。

有了上面這些鋪墊,現(xiàn)在有條件回答顯示亂碼(有些是漢字但并不是你預(yù)期的)的問(wèn)題了。

將代碼清單13-5中的語(yǔ)句:

String reqValue1=new String(reqValue.getBytes("UTF-8"),"UTF-8");

改為:

String reqValue1=new String(reqValue.getBytes("UTF-8"),"GBK");

保存后,再在a.jsp的輸入框中輸入“你”,您將看到圖13.6所示結(jié)果。

在圖13.6中,reqValue1的值變成了“浣?”,雖然其中包括漢字“浣”但這不是我們所期待的“你”字。您一定會(huì)問(wèn)為什么會(huì)出現(xiàn)“浣”字呢?

要回答這個(gè)問(wèn)題,您再瀏覽a_gbk.jsp文件,在輸入框中輸入“浣”字,您看到的結(jié)果將如圖13.7所示。

                      

                                        圖13.6                                                                                                      圖13.7

對(duì)比圖13.6與圖13.7可以清楚地看出,原來(lái),“你”的UTF-8的三個(gè)byte中的前兩個(gè)與“浣”的GBK編碼的兩個(gè)byte值相同。

String reqValue1=new String(reqValue.getBytes("UTF-8"),"GBK");在構(gòu)成字符串時(shí),來(lái)了一個(gè)張冠李戴,將“你”的UTF-8的三個(gè)byte中的前兩個(gè)byte構(gòu)造成“浣”,后面的一個(gè)byte不能對(duì)應(yīng)其他漢字就變成了“?”,因此,“你”就陰錯(cuò)陽(yáng)差地變成了“浣?”。這就是顯示亂碼的原因。當(dāng)然這種現(xiàn)象并不僅僅出現(xiàn)在UTF-8與GBK之間,但隱藏在這些現(xiàn)象背后的原理都是相同的。

 


讀寫(xiě)數(shù)據(jù)庫(kù)時(shí)出現(xiàn)亂碼

現(xiàn)在一些常用的數(shù)據(jù)庫(kù)都支持?jǐn)?shù)據(jù)庫(kù)encoding,也就是說(shuō)在創(chuàng)建數(shù)據(jù)庫(kù)時(shí)可以指定它自己的字符集設(shè)置,數(shù)據(jù)庫(kù)數(shù)據(jù)以指定的編碼形式存儲(chǔ)。當(dāng)應(yīng)用程序訪問(wèn)數(shù)據(jù)庫(kù)時(shí),在入口和出口處都會(huì)有encoding轉(zhuǎn)換。如果在應(yīng)用程序中字符本來(lái)已變成了亂碼,當(dāng)然也就無(wú)法正確地轉(zhuǎn)換為數(shù)據(jù)庫(kù)的字符集了。數(shù)據(jù)庫(kù)的encoding可根據(jù)需要來(lái)設(shè)置,比如要支持簡(jiǎn)、繁體中文、日、韓、英語(yǔ)選GBK,如果還要支持其他語(yǔ)言最好選UTF-8。

 



柴油發(fā)電機(jī)
發(fā)電機(jī)
柴油機(jī)
柴油發(fā)電機(jī)
13636374743(上海)
13291526067(嘉興)