解析WEB開發編碼問題
URL: http://tcking.javaeye.com/blog/726643
在進行web開發的時候經常會遇到亂碼的問題,亂碼一般出現在:
1.寫在jsp檔案中的中文變成亂碼
2.頁面的中文都變成亂碼
3.後台通過request.getParameter()亂碼 編碼的基礎知識
電腦只能以位元組為單位儲存和傳輸資訊,而人需要看的是字串,位元組和字串之間的對應關係就是字元集,例如字元“中”使用UTF-8字元集映射的位元組為:E4 B8 AD 三個位元組,同樣反過來,這三個位元組通過UTF-8字元集映射便能得到“中”這個字元,不同的字元集映射規則不一樣,能表示的範圍也不一樣,例如“中”在GB2312中對應的位元組表示為:D6 D0 兩個位元組,字元和位元組之間的轉換,描述為編碼和解碼:
l 字元->位元組:編碼,例如:“中”的UTF-8編碼為 E4 B8 AD
l 位元組->字元:解碼,例如:位元組數組D0 D6根據GB2312解碼為字元“中”
還有一類編碼稱為URI編碼和URI解碼,不過URI編碼和解碼不是字串和位元組流之間的轉換,而是由一個字串表示另一個字串,例如:
l “中”的UTF-8 URI編碼為 %E4%B8%AD
l 字串%E4%B8%AD根據UTF-8進行URI解碼為字元“中”
可以看出來,URI編碼就是將一個字串用%+對應字元集的編碼組織的字串來表示的,在java中String類有兩個常用的方法進行編碼和解碼:
l getBytes :例如“中”.getBytes(“字元集”),根據指定的字元集進行編碼
l String(bytes[],”字元集”):根據自定的字元集對位元組數組進行解碼 為什麼會出現亂碼問題
瀏覽器和伺服器通過網路相連,瀏覽器請求編碼成位元組流在網路上傳輸,應用伺服器接收到瀏覽器發送過來的位元組流後通過相應的字元集再解碼為字串,如果瀏覽器和伺服器端使用了不同的字元集或者不相容的字元集就會導致亂碼問題,例如,瀏覽器將“中”按照UTF-8編碼為位元組E4 B8 AD,在網路上傳輸,應用伺服器接收到位元組後按照GBK進行解碼,那麼前兩個位元組首先被解碼為“涓”了,這隻是個簡單的過程說明,實際的過程比這要複雜。
因此要搞清web的編碼問題,很有必要先弄清請求的過程和過程中的編碼和解碼。web是請求響應模式,使用者操作瀏覽器,例如點擊一個按鈕提交表單,或者點擊一個超連結,這時瀏覽器就會嚮應用伺服器發送請求,servlet容器接受到請求後,根據web.xml的設定調用相應的應用程式,應用程式根據發送的請求進行一定的邏輯處理後返回給瀏覽器一段html代碼,瀏覽器根據html解析並展現給使用者,這就是一次請求應答的過程,下面對各部分作更加詳細的介紹: 1. 瀏覽器嚮應用伺服器發送請求
瀏覽器嚮應用伺服器發送請求一般通過三種方式:1.提交表單,2.超連結,3.Ajax;
1)表單提交
表單提交又分為post和get兩種方法,
採用post方法時瀏覽器會將表單中的字串,採用頁面的字元集編碼為位元組流發送到伺服器,
採用get方法時,瀏覽器首先會將表單中的值通過頁面的字元集進行URI編碼後拼接到action的URL後面發送到應用伺服器,例如:
|
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>test</title>
</head>
<body>
<form action=http://www.google.com>
<input type=text name=test value="中" />
<input type=submit />
</form>
</body>
</html> |
在點擊提交的時候,URL為 www.google.com.hk/?test=%E4%B8%AD,可以看出“中”字被編碼為%E4%B8%AD,因為當前頁面的字元集為UTF-8,所以在進行get表單提交時將按照UTF-8字元集進行URI編碼。
2)超連結
在超連結中一般會傳遞參數,有時候也會傳遞中文,如這段代碼:<a href="http://www.google.com/?test=中">link</a>,瀏覽器最終發送給伺服器的都是asc字元,其中的“中”不屬於asc字元集,因此也會對其進行URI編碼的,但是不同的瀏覽器採用的字元集卻不一樣,如上面的這個超連結片段,在windows7中,不論頁面的content="text/html; charset=utf-8"還是content="text/html; charset=GBK"IE8發送的都是www.google.com.hk/?test=%D6%D0,可以看出這是GBK的URI編碼,IE8對超連結中的URI編碼與頁面的編碼無關,與系統的預設編碼有關;而在windosx xp中,IE8發送的是頁面編碼採用的字元集進行的URI編碼,如果頁面編碼為GBK,IE6發送的為GBK的頁面編碼,如果頁面編碼為UTF-8則只發送UTF-8的URI編碼的前兩個位元組;在其他的瀏覽器中,如Firefox和chrome,其URI編碼採用的是頁面的編碼。
|
作業系統 |
瀏覽器 |
頁面編碼 |
發送的請求字串 |
說明 |
|
windows7 |
IE8中文 |
UTF-8 |
test=%D6%D0 |
windows7中,採用GBK字元集進行URI編碼與頁面的編碼無關 |
|
windows7 |
IE8中文 |
GBK |
test=%D6%D0 |
|
XP |
IE8中文 |
UTF-8 |
test=%E4%B8%AD |
xp中與採用頁面編碼的字元集進行URI編碼 |
|
XP |
IE8中文 |
GBK |
test=%D6%D0 |
|
windows2003 |
IE6中文 |
GBK |
test=%D6%D0 |
GBK是正確,UTF-8是不正確 |
|
windows2003 |
IE6中文 |
UTF-8 |
test=%E4%B8 |
|
-- |
chrome中文,firefox英文 |
UTF-8 |
test=%E4%B8%AD |
採用頁面編碼的字元集進行URI編碼 |
|
-- |
chrome中文,firefox英文 |
GBK |
test=%D6%D0 |
可以看出,直接在URL中帶中文,IE的不同版本在不同的作業系統中進行URI編碼的結果可能不一樣,chrome和firefox使用編碼和表單的get方式的編碼一致,因此,直接在連結中寫非asc字元是很危險的,因為字元的編碼方式和用戶端的環境有關。所以為了避免瀏覽器進行不確定的URI編碼,需要在程式中將中文進行URI編碼後在放到URL中,JavaScript提供了encodeURI()函數,它提供的是UTF-8的URI編碼,也可以通過java.net.URLEncoder.encode(str,"字元集")進行編碼
3)ajax
ajax可以指定get方式或者post方式,情況和上面說的類似 2. 應用伺服器擷取參數
在servlet中一般通過request.getParameter()來獲得瀏覽器發送過來的參數,需要注意的是,伺服器Servlet的最底層接受到的是InputStream,也就是位元組流,request.getParameter()返回的是一個字串,因此在getParameter()方法的內部存在解碼的過程,而解碼所採用的字元集根據應用伺服器和作業系統的不同有可能不同,