三、Java Web中出现的一些乱码问题总结(详解)
一、response.getWriter().write()和 response.getWriter().print()的區別
response.getWriter()返回的是PrintWriter,這是一個打印輸出流
response.getWriter().write()和 response.getWriter().print()是響應給客戶端的東西,如果不用ajax接收將數據放在合適的位置,就會在瀏覽器上生成一個新的頁面來顯示內容。
print
response.getWriter().print(),不僅可以打印輸出文本格式的(包括html標簽),還可以將一個對象以默認的編碼方式轉換為二進制字節輸出
writer
response.getWriter().writer(),只能打印輸出文本格式的(包括html標簽),不可以打印對象
二、常見亂碼問題分析
1、中文變成看不懂的字符
如果一串中文字符變成了一串看不懂的字符如:“ì ? £ ?? ò ?2?? £ ?”,這種情況通常是編碼
字符集與解碼時所用的字符集不一致所造成的。比如使用GBK編碼,如果使用ISO-8859-1解碼
的話結果就是這樣。
2、一個漢字變成了一個問號
如果編碼和解碼的字符集都是一致的,那么可以確定該字符編碼不支持中文,例如:ISO-8859-1
3、一個漢字變成了兩個問號
中文經過多次編碼且其中有一次編碼或者解碼使用了不支持中文的字符集
三、亂碼相關知識點:
1.UTF-8國際編碼,GBK中文編碼。GBK包含GB2312,即如果通過GB2312編碼后可以通過GBK解碼,反之可能不成立;
2、web tomcat:默認是ISO8859-1,不支持中文的
3.java.nio.charset.Charset.defaultCharset() 獲得平臺默認字符編碼;
4.getBytes() 是通過平臺默認字符集進行編碼;
5.碼表:是一種規則,用來讓我們看得懂的語言轉換為電腦能夠認識的語言的一種規則,有很多中碼表,IS0-8859-1,GBK,UTF-8,UTF-16等一系列碼表,比如GBK,UTF-8,UTF-16都可以標識一個漢字,而如果要標識英文,就可以用IS0-8859-1等別的碼表。
6.編碼:將我們看得懂的語言轉換為電腦能夠認識的語言。這個過程就是編碼的作用
7.解碼:將電腦認識的語言轉換為我們能看得懂得語言。這個過程就是解碼的作用
瀏覽器使用的是UTF-8碼表,通過http協議傳輸,http協議只支持IS0-8859-1,到了服務器,默認也是使用的是IS0-8859-1的碼表,看圖
也就是三個過程,經歷了兩次編碼,所以就需要進行兩次解碼,
1、瀏覽器將"小明"使用UTF-8碼表進行編碼(因為小明這個是漢字,所以使用能標識中文的碼表,這也是我們可以在瀏覽器上可以手動設置的,如果使用了不能標識中文的碼表,那么就將會出現亂碼,因為碼表中找不到中文對應的計算機符號,就可能會用??等其他符號表示),編碼后得到的為 1234 ,將其通過http協議傳輸。
2、在http協議傳輸,只能用ISO-8859-1碼表中所代表的符號,所以會將我們原先的1234再次進行一次編碼,這次使用的是ISO-8859-1,得到的為 ??? ,然后傳輸到服務器
3、服務器獲取到該數據是使用UTF-8編碼的,而tomcat服務器默認使用ISO-8859-1解碼,由于編碼和解碼所使用的字符集不一致,所以會出現亂碼問題。
解決方案: 如果我們在客戶端使用UTF-8編碼的JSP頁面發出請求,瀏覽器編碼后的UTF-8字節會以ISO-8859-1的形式傳遞到服務器端。所以要得到經HTTP協議傳輸的原始字節,我們需要先調用getBytes(“ISO-8859-1”)得到原始的字節,但由于我們客戶端的原始編碼是UTF-8,如果繼續按照ISO-8859-1解碼,那么得到的將不是一個中文字符,而是3個亂碼的字符。所以我們需要再次調用new String(bytes,“UTF-8”),將字節數組按照UTF-8的格式,每3個一組進行解碼,才能還原為客戶端的原始字符。
???.getBytes(“ISO-8859-1”);//第一次解碼,轉換為電腦能夠識別的語言,
new String(1234,“UTF-8”);//第二次解碼,轉換為我們認識的語言
四、Servlet相關的幾種亂碼
1、 瀏覽器調用jsp,html等頁面中文顯示亂碼
解決這類亂碼需要滿足兩個要求:
1)文件本身是以utf-8編輯保存的(myEclipse中在properties中鼠標右鍵選擇utf-8)
2)瀏覽器用utf-8解析:
(手動)==> 在瀏覽器中右鍵選擇編碼格式為utf-8
(智能)==> 在文件中寫入如: 通過標簽模擬response頭,起到告訴瀏覽器用utf-8的編碼解析
(智能)==> response.setContentType(“text/html;charset=UTF-8”);起到告訴瀏覽器用utf-8的編碼解析
常用:
<meta name="content-type" content="text/html; charset=UTF-8">或<meta charset="utf-8"> <%@ pageEncoding="utf-8"%> <?xml encoding="UTF-8"?>2、通過瀏覽器調用servlet,頁面顯示亂碼。
Servlet亂碼分為request亂碼和response亂碼:
(1)response響應回瀏覽器出現的中文亂碼:
首先介紹一下,response對象是如何向瀏覽器發送數據的。兩種方法,一種getOutputStream,一種getWriter。
1)ServletOutputStream getOutputStream(); //獲取輸出字節流。提供write() 和 print() 兩個輸出方法
2)PrintWriter getWriter(); //獲取輸出字符流 提供write() 和 print()兩個輸出方法
print()方法底層都是使用write()方法的,相當于print()方法就是將write()方法進行了封裝,使開發者更方便快捷的使用,想輸出什么,就直接選擇合適的print()方法,而不用考慮如何轉換字節。
1、ServeltOutputStream getOutputStream();不能直接輸出中文,直接輸出中文會報異常
解決:
resp.getoutputStream().write(“哈哈哈,我要輸出到瀏覽器”.getBytes(“UTF-8”));
要輸出的漢字先用UTF-8進行編碼,而不用讓tomcat來進行編碼,這樣如果瀏覽器用的是UTF-8碼表進行解碼的話,那么就會正確輸出,如果瀏覽器用的不是UTF-8,那么還是會出現亂碼,所以說這個關鍵要看瀏覽器用的什么碼表,這個就不太好,這里還要注意一點,就是使用的是write(byte)方法,因為print()方法沒有輸出byte類型的方法。
注意:response.setContentType 等效于 response.setCharacterEncoding + response.setHeader
解決方法:
方法一:response.setContentType(“text/html;charset=uft-8”);
使用Servlet API 來通知tomcaat和強制瀏覽器使用UTF-8來進行編碼解碼,這個的底層代碼就是 response.setCharacterEncoding + response.setHeader的代碼,進行了簡單的封裝而已。
/*通過設置響應頭,來設置瀏覽器也使用UTF-8字符集 目的是為了控制瀏覽器的行為,即控制瀏覽器用UTF-8進行解碼response.setContentType 等效于 response.setCharacterEncoding + response.setHeader因為在setContentType方法中已經調用了setCharacterEncoding方法設置了Response容器的編碼了。注意:1.response.setContentType它會通是設置服務器和客戶端都使用UTF-8字符集,2.還設置了響應頭3.調用response.setCharacterEncoding 和 response.setContentType方法,必須在getWriter執行之前或者response被提交之前,否則依舊會出現亂碼問題*/response.setContentType("text/html;charset=UTF-8");方法二:使用Servlet API response.setCharacterEncoding(“UTF-8”);
讓tomcat將我們要響應到瀏覽器的中文用UTF-8進行編碼,而不使用默認的ISO-8859-1了,這個還是要取決于瀏覽器是不是用的UTF-8的碼表,跟上面的一樣有缺陷,
所以需要配合response.setHeader(“Content-Type”,“text/html;charset=UTF-8”);來使用,作用為手動設置響應內容,通知tomcat和瀏覽器使用utf-8來進行編碼和解碼。
(setHeader是HttpServletResponse的方法。如果想在攔截器Filter中設置字符編碼,則無此方法,因為Filter的doFilter方法的參數類型是ServletResponse)
這里有幾個小細節需要注意:
response.setCharacterEncoding(“UTF-8”);需要寫在PrintWriter out = response.getWriter();的前面。拿到字符流后再設置編碼是沒有用的。
response.setContentType(“text/html;charset=UTF-8”);這句代碼其實有兩個作用:通知response以UTF-8輸出和瀏覽器以UTF-8打開。即等價于response.setHeader(“content-type”, “text/html;charset=UTF-8”);和response.setCharacterEncoding(“UTF-8”);兩句代碼。
response.setContentType(“text/html;charset=UTF-8”); 目的是為了控制瀏覽器的行為,即控制瀏覽器用UTF-8進行解碼;
response.setCharacterEncoding(“UTF-8”);目的是用于response.getWriter()輸出的字符流的亂碼問題。如果是response.getOutputStream()是不需要此種解決方案的,因為這句話的意思是為了將response對象中的數據以UTF-8解碼后的字節流發向瀏覽器;
(2)request亂碼問題
request請求分為post和get,對于不同的請求方式有不同的解決亂碼的方案
第一種:POST請求
post請求方式的參數是在請求體中,相對于get請求簡單很多,沒有經過http協議這一步的編碼過程,所以只需要在服務器端,設置服務器解碼的碼表跟瀏覽器編碼的碼表是一樣的就行了,在這里瀏覽器使用的是UTF-8碼表編碼,那么服務器端就設置解碼所用碼表也為UTF-8就OK了
解決方案:
request.setCharacterEncoding(“UTF-8”);//命令Tomcat使用UTF-8碼表解碼,而不用默認的ISO-8859-1了。
所以在很多時候,在doPost方法的第一句,就是這句代碼,防止獲取請求參數時亂碼。
注意:該設置只對post請求有效
第二種:GET請求(URI方式傳遞參數亂碼),TOMCAT 8.0 以上用 GET 方式請求已經不存在亂碼問題
get請求的參數是在url后面提交過來的,也就是在請求行中
FormServlet是一個普通的Servlet,瀏覽器訪問它時,使用get請求方式提交了一個name=小明的參數值,在doGet中獲取該參數值,并且打印到控制臺,發現出現亂碼
法一:要解決這個問題,修改tomcat服務器的配置文件。
[注意] TOMCAT 8.0 以上用 GET 方式請求,傳送的參數已默認為 UTF-8 編碼,所以可以不需要修改配置文件
修改tomcat目錄下的conf/server.xml文件的第69行:
修改前內容:
<Connector port="8080" protocol="HTTP/1.1"maxThreads="150" connectionTimeout="200000"redirecPort="8443"/>修改后內容:<Connector port="8080" protocol="HTTP/1.1"maxThreads="150" connectionTimeout="200000"redirecPort="8443" URIEncoding="utf-8"/>法二:
String username= request.getParameter(“name”);
String usernameString = new String(username.getBytes(“ISO-8859-1”),“UTF-8”);
tomcat默認全部都是用ISO-8859-1編碼,不管你頁面用什么顯示,Tomcat最終還是會替你將所有字符轉做ISO-8859-1.那么,當在另目標頁面再用GBK翻譯時就會將本來錯的編碼翻譯成GBK的編碼,這時的文字會亂碼.
1.xxx.getBytes( )叫做編碼,new String(byte[ ],encodingname)這個叫解碼
2.http協議不會對請求參數進行編解碼!只是傳輸,傳輸的是二進制。解碼是tomcat的工作,utf8編碼的字節序列被tomcat默認以iso8895-1方式解碼所以有了亂碼,所以要重新編碼再解碼。
a. 所以需要先將得到"字符"(不管是什么)都先用字節數組表示,
b. 且使用ISO-8859-1進行翻譯,得到一個在ISO-8859-1編碼環境下的字節數組.例如:AB表示成[64,65].
c. 然后再用GBK編碼這個數組,并翻譯成一個字符串.
那么我們可以得到一個編碼轉換的過程
假設:GBK碼(“你”)->URLencode后變成->(%3F%2F)->Tomcat自動替你轉一次ISO-8859-1->得到( 23 43 68 23 42 68 每一個符號表示為ISO-8859-1中的一個編碼)->接收頁面—>再轉一次為ISO-8859-1的Byte數組[23,43,68,23,42,68]—>用GBK再轉為可讀的文字—>(%3F%2F"---->轉為(“你”)
法三:URL轉換
String username= request.getParameter(“name”);
String usernameString = new String(username.getBytes(“ISO-8859-1”),“gb2312”);
**
因為瀏覽器默認編碼為gb2312
小結請求參數亂碼問題:
get請求和post請求方式的中文亂碼問題處理方式不同
1)get:請求參數在請求行中,涉及了http協議,手動解決亂碼問題,知道出現亂碼的根本原因,對癥下藥,其原理就是進行兩次編碼,兩次解碼的過程
new String(xxx.getBytes(“ISO-8859-1”),“UTF-8”);
2)post:請求參數在請求體中,使用servlet API解決亂碼問題,其原理就是一次編碼一次解碼,命令tomcat使用特定的碼表解碼。
request.setCharaterEncoding(“UTF-8”);
五、總結
1)如果是post請求,加上這兩句基本上可以解決所有的亂碼問題
response.setContentType("text/html;charset=UTF-8");request.setCharacterEncoding("UTF-8");2)如果是get請求,TOMCAT 8.0 以上用 GET 方式請求,傳送的參數已默認為 UTF-8 編碼,所以不會出現get請求亂碼問題,可以不需要修改配置文件。
a. 修改tomcat服務器的配置文件后 再加上 response.setContentType(“text/html;charset=UTF-8”);
b. 不想修改配置文件的話,可以使用下面的方式:
response.setContentType("text/html;charset=UTF-8"); String username= request.getParameter("name"); String usernameString = new String(username.getBytes("ISO-8859-1"),"UTF-8");請求亂碼
1)get請求:
經過了兩次編碼,所以就要兩次解碼
第一次解碼:xxx.getBytes(“ISO-8859-1”);得到yyy
第二次解碼:new String(yyy,“utf-8”);
連續寫:new String(xxx.getBytes(“ISO-8859-1”),“UTF-8”);
2)post請求:
只經過一次編碼,所以也就只要一次解碼,使用Servlet API request.setCharacterEncoding();
request.setCharacterEncoding("UTF-8"); //不一定解決,取決于瀏覽器是用什么碼表來編碼,瀏覽器用UTF-8,那么這里就寫UTF-8。響應亂碼
1)getOutputStream();
使用該字節輸出流,不能直接輸出中文,會出異常,要想輸出中文,解決方法如下
解決:getOutputStream().write(xxx.getBytes(“UTF-8”));//手動將中文用UTF-8碼表編碼,變成字節傳輸,變成字節后,就不會報異常,并且tomcat也不會在編碼,因為已經編碼過了,所以到瀏覽器后,如果瀏覽器使用的是UTF-8碼表解碼,那么就不會出現中文亂碼,反之則出現中文亂碼,所以這個方法,不能完全保證中文不亂碼
2)getWrite();
使用字符輸出流,能直接輸出中文,不會出異常,但是會出現亂碼。能用三種方法解決,一直使用第二種方法
解決:通知tomcat和瀏覽器使用同一張碼表。
response.setContentType(“text/html;charset=utf-8”); //通知瀏覽器使用UTF-8解碼 同時通知tomcat和瀏覽器使用UTF-8編碼和解碼。
這個方法的底層原理是這句話:response.setHeader(“contentType”,“text/html;charset=utf-8”);
注意:getOutputStream()和getWrite() 這兩個方法不能夠同時使用,一次只能使用一個,否則報異常
總結
以上是生活随笔為你收集整理的三、Java Web中出现的一些乱码问题总结(详解)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue-cli4.x 中 配置允许跨域请
- 下一篇: java美元兑换,(Java实现) 美元