服務(wù)器返回亂碼頁(yè)面,請(qǐng)求的數(shù)據(jù)發(fā)送到服務(wù)器后取出來(lái)是亂碼,以上兩個(gè)問(wèn)題是web開(kāi)發(fā)人員經(jīng)常遇到的問(wèn)題,解決這類問(wèn)題需要理解亂碼問(wèn)題的根源所在。
1. 字符編碼
字符是以二進(jìn)制編碼的形式保存在存儲(chǔ)器中的,如:“我”這個(gè)字,可以用gbk的方式保存(用字節(jié)表示是[-50, -46]),也可以用utf-8的方式保存(用字節(jié)表示是[-26, -120, -111])。程序在讀取數(shù)據(jù)塊時(shí)需要一個(gè)字節(jié)一個(gè)字節(jié)的讀取,然后將字節(jié)轉(zhuǎn)換為字符,顯然如果程序不知道字節(jié)是表示的什么編碼的字符,讀出來(lái)就會(huì)出問(wèn)題,這就如同你說(shuō)了一句話,我如果事先不知道你說(shuō)的將是什么語(yǔ)言,就沒(méi)法去翻譯了,如果你說(shuō)的英語(yǔ),我以為是日語(yǔ),然后按日語(yǔ)的方式來(lái)理解到我的主觀意識(shí),顯然結(jié)果就是不知你在說(shuō)什么了,就成了我們程序中的亂碼。所以對(duì)于前面我所表示的字節(jié)數(shù)組byte[] data = {-26, -120, -111},我們必須這樣做才能得到正確的字符: String s = new String(data, "utf-8"),(注:這里用了字符串,因?yàn)樽址褪怯梢粋€(gè)個(gè)字符組成的),如果我們不指定參數(shù)里面的"utf-8",那么系統(tǒng)就會(huì)用操作系統(tǒng)默認(rèn)的編碼了,這可能是gbk或是什么任何編碼。
2. jsp服務(wù)器返回亂碼頁(yè)面
2.1 pageEncoding
在jsp頁(yè)面的page指令中我們指定了pageEncoding屬性,這個(gè)屬性就是告訴jsp容器如何讀取這個(gè)jsp頁(yè)面,所以這個(gè)屬性必須與jsp頁(yè)面保存的編碼保持一致。也就是說(shuō),如果你頁(yè)面的編碼保存為gbk, 而pageEncoding設(shè)置成了utf-8,則jsp容器在讀這個(gè)jsp頁(yè)面的時(shí)候就會(huì)出錯(cuò)(如果存在非英文字符的話)。jsp容器讀jsp的目的是將其翻譯成java代碼,所以如果讀錯(cuò)了jsp頁(yè)面,翻譯出來(lái)的java代碼也就會(huì)出錯(cuò),如果這種錯(cuò)誤影響了java文件的語(yǔ)法,就會(huì)在訪問(wèn)時(shí)出現(xiàn)無(wú)法編譯jsp的語(yǔ)法錯(cuò)誤,如果沒(méi)有影響到語(yǔ)法,就會(huì)出現(xiàn)最終顯示的html頁(yè)面上有亂碼的錯(cuò)誤。所以如果遇到顯示亂碼,則檢查pageEncoding是否正確。
2.2 contentType
page指令中的contentType屬性用于指定返回給瀏覽器的數(shù)據(jù)的文檔類型,服務(wù)器通過(guò)http頭信息返回給瀏覽器這個(gè)信息,所以在瀏覽器 html代碼中用戶是看不到的。同時(shí)contentType屬性還可以指定頁(yè)面的編碼,即服務(wù)器即以什么編碼發(fā)送頁(yè)面數(shù)據(jù)。比如說(shuō)中文數(shù)據(jù),可以用gbk 或utf-8的方式來(lái)發(fā)送,這個(gè)編碼跟jsp頁(yè)面的編碼沒(méi)有關(guān)系,只要設(shè)定的編碼支持頁(yè)面中的字符就行了。相同于有了一個(gè)字符串s="中國(guó)人",然后用 s.getBytes("gbk")的方式來(lái)發(fā)送s。所以由于contentType錯(cuò)誤出現(xiàn)亂碼的概念不高,但也要注意一下,比如說(shuō)如果設(shè)置成了"iso8859-1",則瀏覽器就會(huì)顯示亂碼了。contentType還有一個(gè)用,就是瀏覽器將會(huì)依據(jù)這個(gè)編碼來(lái)顯示頁(yè)面,在IE下點(diǎn)右鍵,然后選擇“編碼”,你就可以注意到頁(yè)面是以什么編碼顯示的了。
2.3 如果是servlet返回的結(jié)果
上面說(shuō)的是jsp,如果servlet的話就要注意設(shè)置response.setCharacterEncoding(""),如果沒(méi)有設(shè)置,服務(wù)器會(huì)默認(rèn)為是iso8859-1,設(shè)置后得到的writer(即response.getWriter())對(duì)象,就會(huì)依據(jù)這個(gè)編碼來(lái)向客戶端寫(xiě)數(shù)據(jù),writer對(duì)象的構(gòu)造與以下方式類似:PrintWriter pw = new PrinterWriter(new OutputStreamWriter(socket.getOutputStream(), "編碼")),這里提到了通過(guò)socket得到輸出流,不明白的話可以參考我的另一篇文章。pw.write("你好"),實(shí)際上就是先通過(guò)byte[] data = "你好".getBytes("編碼"),然后將data寫(xiě)給客戶端。
)
3. 服務(wù)器得到客戶端傳過(guò)來(lái)的數(shù)據(jù)為亂碼
3.1 通用解決方案
String param = request.getParameter("paramName"),如果瀏覽器傳過(guò)來(lái)的為中文,則取出來(lái)的數(shù)據(jù)將是亂碼。為什么呢?因?yàn)榭蛻舳酥荒軐?shù)據(jù)的編碼傳給服務(wù)器,如[-26, -120, -111],但服務(wù)器并不知道這是什么字符集的編碼,于是假定為iso8859-1, 用這種方式構(gòu)造了字符串s = new String(data, "iso8859-1"),顯然這樣肯定是亂碼。解決方案很簡(jiǎn)單,我們得到值param后,用byte data[] = param.getBytes("iso8859-1"),這樣data就是客戶端傳過(guò)來(lái)的真實(shí)編碼,然后我們?cè)僦匦聞?chuàng)建字符串:param = new String(data, "正確的編碼");
3.2 POST請(qǐng)求
如于post請(qǐng)求處理起來(lái)更簡(jiǎn)單,get請(qǐng)求與post請(qǐng)求向服務(wù)器發(fā)送數(shù)據(jù)的方式不一樣,get請(qǐng)求的參數(shù)是通過(guò)HTTP頭信息中的第一行數(shù)據(jù)發(fā)送的,是URI的一部分,而post請(qǐng)求則是在發(fā)送完HTTP頭信息后作為單獨(dú)的數(shù)據(jù)塊發(fā)送的。因此對(duì)于get請(qǐng)求的參數(shù),我們?cè)谑褂胷equest之前服務(wù)器已經(jīng)讀出來(lái)了,已經(jīng)是亂碼了,只能用前面的方案,但對(duì)于post請(qǐng)求的數(shù)據(jù),我們?cè)谡{(diào)用getParameter或getReader之前,服務(wù)器并沒(méi)有去處理,所以我們可以在getParameter之前先告訴服務(wù)器正確的編碼,通過(guò)request.setCharacterEncoding("正確的編碼"),然后再讀取參數(shù)。
4 AJax
對(duì)于ajax請(qǐng)求注意要用utf-8編碼,request和response都需要使用utf-8
1. 字符編碼
字符是以二進(jìn)制編碼的形式保存在存儲(chǔ)器中的,如:“我”這個(gè)字,可以用gbk的方式保存(用字節(jié)表示是[-50, -46]),也可以用utf-8的方式保存(用字節(jié)表示是[-26, -120, -111])。程序在讀取數(shù)據(jù)塊時(shí)需要一個(gè)字節(jié)一個(gè)字節(jié)的讀取,然后將字節(jié)轉(zhuǎn)換為字符,顯然如果程序不知道字節(jié)是表示的什么編碼的字符,讀出來(lái)就會(huì)出問(wèn)題,這就如同你說(shuō)了一句話,我如果事先不知道你說(shuō)的將是什么語(yǔ)言,就沒(méi)法去翻譯了,如果你說(shuō)的英語(yǔ),我以為是日語(yǔ),然后按日語(yǔ)的方式來(lái)理解到我的主觀意識(shí),顯然結(jié)果就是不知你在說(shuō)什么了,就成了我們程序中的亂碼。所以對(duì)于前面我所表示的字節(jié)數(shù)組byte[] data = {-26, -120, -111},我們必須這樣做才能得到正確的字符: String s = new String(data, "utf-8"),(注:這里用了字符串,因?yàn)樽址褪怯梢粋€(gè)個(gè)字符組成的),如果我們不指定參數(shù)里面的"utf-8",那么系統(tǒng)就會(huì)用操作系統(tǒng)默認(rèn)的編碼了,這可能是gbk或是什么任何編碼。
2. jsp服務(wù)器返回亂碼頁(yè)面
2.1 pageEncoding
在jsp頁(yè)面的page指令中我們指定了pageEncoding屬性,這個(gè)屬性就是告訴jsp容器如何讀取這個(gè)jsp頁(yè)面,所以這個(gè)屬性必須與jsp頁(yè)面保存的編碼保持一致。也就是說(shuō),如果你頁(yè)面的編碼保存為gbk, 而pageEncoding設(shè)置成了utf-8,則jsp容器在讀這個(gè)jsp頁(yè)面的時(shí)候就會(huì)出錯(cuò)(如果存在非英文字符的話)。jsp容器讀jsp的目的是將其翻譯成java代碼,所以如果讀錯(cuò)了jsp頁(yè)面,翻譯出來(lái)的java代碼也就會(huì)出錯(cuò),如果這種錯(cuò)誤影響了java文件的語(yǔ)法,就會(huì)在訪問(wèn)時(shí)出現(xiàn)無(wú)法編譯jsp的語(yǔ)法錯(cuò)誤,如果沒(méi)有影響到語(yǔ)法,就會(huì)出現(xiàn)最終顯示的html頁(yè)面上有亂碼的錯(cuò)誤。所以如果遇到顯示亂碼,則檢查pageEncoding是否正確。
2.2 contentType
page指令中的contentType屬性用于指定返回給瀏覽器的數(shù)據(jù)的文檔類型,服務(wù)器通過(guò)http頭信息返回給瀏覽器這個(gè)信息,所以在瀏覽器 html代碼中用戶是看不到的。同時(shí)contentType屬性還可以指定頁(yè)面的編碼,即服務(wù)器即以什么編碼發(fā)送頁(yè)面數(shù)據(jù)。比如說(shuō)中文數(shù)據(jù),可以用gbk 或utf-8的方式來(lái)發(fā)送,這個(gè)編碼跟jsp頁(yè)面的編碼沒(méi)有關(guān)系,只要設(shè)定的編碼支持頁(yè)面中的字符就行了。相同于有了一個(gè)字符串s="中國(guó)人",然后用 s.getBytes("gbk")的方式來(lái)發(fā)送s。所以由于contentType錯(cuò)誤出現(xiàn)亂碼的概念不高,但也要注意一下,比如說(shuō)如果設(shè)置成了"iso8859-1",則瀏覽器就會(huì)顯示亂碼了。contentType還有一個(gè)用,就是瀏覽器將會(huì)依據(jù)這個(gè)編碼來(lái)顯示頁(yè)面,在IE下點(diǎn)右鍵,然后選擇“編碼”,你就可以注意到頁(yè)面是以什么編碼顯示的了。
2.3 如果是servlet返回的結(jié)果
上面說(shuō)的是jsp,如果servlet的話就要注意設(shè)置response.setCharacterEncoding(""),如果沒(méi)有設(shè)置,服務(wù)器會(huì)默認(rèn)為是iso8859-1,設(shè)置后得到的writer(即response.getWriter())對(duì)象,就會(huì)依據(jù)這個(gè)編碼來(lái)向客戶端寫(xiě)數(shù)據(jù),writer對(duì)象的構(gòu)造與以下方式類似:PrintWriter pw = new PrinterWriter(new OutputStreamWriter(socket.getOutputStream(), "編碼")),這里提到了通過(guò)socket得到輸出流,不明白的話可以參考我的另一篇文章。pw.write("你好"),實(shí)際上就是先通過(guò)byte[] data = "你好".getBytes("編碼"),然后將data寫(xiě)給客戶端。
)
3. 服務(wù)器得到客戶端傳過(guò)來(lái)的數(shù)據(jù)為亂碼
3.1 通用解決方案
String param = request.getParameter("paramName"),如果瀏覽器傳過(guò)來(lái)的為中文,則取出來(lái)的數(shù)據(jù)將是亂碼。為什么呢?因?yàn)榭蛻舳酥荒軐?shù)據(jù)的編碼傳給服務(wù)器,如[-26, -120, -111],但服務(wù)器并不知道這是什么字符集的編碼,于是假定為iso8859-1, 用這種方式構(gòu)造了字符串s = new String(data, "iso8859-1"),顯然這樣肯定是亂碼。解決方案很簡(jiǎn)單,我們得到值param后,用byte data[] = param.getBytes("iso8859-1"),這樣data就是客戶端傳過(guò)來(lái)的真實(shí)編碼,然后我們?cè)僦匦聞?chuàng)建字符串:param = new String(data, "正確的編碼");
3.2 POST請(qǐng)求
如于post請(qǐng)求處理起來(lái)更簡(jiǎn)單,get請(qǐng)求與post請(qǐng)求向服務(wù)器發(fā)送數(shù)據(jù)的方式不一樣,get請(qǐng)求的參數(shù)是通過(guò)HTTP頭信息中的第一行數(shù)據(jù)發(fā)送的,是URI的一部分,而post請(qǐng)求則是在發(fā)送完HTTP頭信息后作為單獨(dú)的數(shù)據(jù)塊發(fā)送的。因此對(duì)于get請(qǐng)求的參數(shù),我們?cè)谑褂胷equest之前服務(wù)器已經(jīng)讀出來(lái)了,已經(jīng)是亂碼了,只能用前面的方案,但對(duì)于post請(qǐng)求的數(shù)據(jù),我們?cè)谡{(diào)用getParameter或getReader之前,服務(wù)器并沒(méi)有去處理,所以我們可以在getParameter之前先告訴服務(wù)器正確的編碼,通過(guò)request.setCharacterEncoding("正確的編碼"),然后再讀取參數(shù)。
4 AJax
對(duì)于ajax請(qǐng)求注意要用utf-8編碼,request和response都需要使用utf-8
java.lang.ClassNotFoundException: com.opensymphony.xwork2.util.TextUtils