週末宅在家裏睡完覺就吃飯,吃完飯接着睡覺,這日子過的實在是沒勁啊。明明還有計劃中的事情沒有作, 爲何就是不想去作呢,這樣的生活持續下去,必然會成爲一個徹頭徹尾的loser。上一篇寫的 初識編碼 ,這一篇把web編碼寫出來和菜鳥們分享一下。圖片比較多,手機用戶不要看,流量沒了俺不負責。javascript
一.html頁面編碼html
當瀏覽器請求一個靜態的html頁面時,服務器會將html頁面的字節流經過網絡傳輸給瀏覽器。瀏覽器再將字節流解碼成相應的html文本字符,而後將html元素渲染出來。在這個流程中瀏覽器有一個解碼html字節流的過程。瀏覽器如何判斷用什麼編碼格式去解碼呢?在html頁面的head標籤中一般會包含一個指定頁面編碼的<meta/>標籤,若是指定的是utf-8編碼,瀏覽器就用utf-8解碼html頁面。問題是在瀏覽器解析<meta/>標籤得到頁面編碼以前,瀏覽器是用什麼編碼來解析<meta/>標籤的呢?由於html開頭都是字母,基本不會存在ASCII碼以外的字符,因此識別起來仍是沒有難度的。
java
以下是個人html代碼,第5行和第6行,會根據不一樣的測試條件分別打開註釋,具體打開哪一個後邊會有提示。web
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <!-- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -->
6 <!-- <meta http-equiv="Content-Type" content="text/html; charset=GBK" /> -->
7 <title>utf-8 html</title>
8 </head>
9 <body>
10 <p>Get 請求</p>
11 <form action="TestServlet" method="get">
12 <input type="text" value="你好" name="txtKey"/>
13 <input type="submit" value="提交"/>
14 </form>
15 <p>Post 請求</p>
16 <form action="TestServlet" method="post">
17 <input type="text" value="你好" name="txtKey"/>
18 <input type="submit" value="提交"/>
19 </form>
20 </body>
21 </html>
演示過程用的環境是tomcat+servlet,瀏覽器是chrome。項目結構以下,其中有兩個html頁面,index.htm頁面用utf-8編碼存儲,lindexGBK.html頁面用GBK編碼存儲。兩個頁面的html代碼都是上邊給出的html代碼。chrome
實驗一: 在utf-8編碼的html頁面中指定不一樣的編碼格式瀏覽器
啓動項目,用瀏覽器打開index.html的url,如左下圖所示,能夠看出瀏覽器解析到<meta/>標籤中的utf-8編碼,並用utf-8解碼html頁面,頁面正確顯示。若是在<meta/>標籤中指定頁面編碼爲GBK,那麼瀏覽器就用GBK解碼,確定就不能正確顯示頁面了,如右下圖所示,頁面中文出現亂碼。若是想避免亂碼的話,仍是必須保證編解碼格式統一。tomcat
實驗二: 不指定頁面編碼的狀況下測試不一樣的編碼頁面服務器
此次index.html和indexGBK.html都沒有指定頁面編碼。用瀏覽器打開index.html對應的url,如左下圖所示,能夠看出瀏覽器用utf-8解碼html頁面,頁面正確顯示。用瀏覽器打開indexGBK.html對應的url,瀏覽器也是用utf-8解碼,確定就不能正確顯示頁面了,如右下圖所示,頁面中文出現亂碼。由於在沒有指定頁面編碼的狀況下,瀏覽器採用操做系統默認編碼,個人系統就是utf-8編碼,因此index.html頁面就碰巧解析正確了。網絡
實驗三: 在一個html頁面中指定兩個不一樣的編碼post
用瀏覽器打開index.html對應的url,如左下圖所示,有兩個<meta/>標籤指定了不一樣的頁面編碼。當utf-8編碼在GBK編碼上面時,能夠看出瀏覽器用utf-8解碼html頁面,頁面正確顯示。如右下圖所示,若是GBK編碼在utf-8編碼上面,那麼瀏覽器就用GBK解碼,頁面中文出現亂碼。因此若是出現多個編碼以第一個出現的爲主,固然這種狀況是不可能出現的(出現了也是代碼問題),爲何會出現這種狀況我以爲須要解釋一下,由於瀏覽器在解析<meta/>標籤時一旦發現頁面編碼,立馬就用這個編碼解析頁面。
html的form在沒有指定accept-charset屬性的狀況下,html的表單提交採用的編碼也是指定的頁面編碼,若是沒有指定頁面編碼,也是採用操做系統的默認編碼。總之,html頁面的加載與表單提交採用的編碼格式是同樣的。由於瀏覽器認爲你指定的頁面編碼就是html文件的實際編碼,因此不論是否亂碼,它都會照作。
與html頁面編碼相關的還有js文件編碼,默認狀況下,瀏覽器採用html的編碼格式去解碼js文件。若是js文件與html頁面的編碼不一樣怎麼辦?script標籤有一個charset屬性,這個屬性就決定了瀏覽器去加載完js,解析字節流時用的編碼。
<script type="text/javascript" src="myscripts.js" charset="UTF-8"/>
二.Web後臺編碼
示例代碼是Java寫的,其餘語言也是一個道理,不耽誤理解。請求後臺無非是GET或POST方法,原理是同樣的,我只介紹POST。
html頁面是utf-8格式的, 頁面編碼也指定爲utf-8。
實驗一:後臺解碼
後臺響應代碼以下所示,頁面以post方式請求時會觸發doPost方法。
1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 System.out.println("START..."); 3 String param=request.getParameter(PARAM); 4 System.out.println("原始參數"); 5 System.out.println(param); 6 System.out.println("utf-8解碼"); 7 System.out.println(new String(param.getBytes("ISO-8859-1"),"utf-8")); 8 System.out.println("GBK解碼"); 9 System.out.println(new String(param.getBytes("ISO-8859-1"),"GBK")); 10 System.out.println("END..."); 11 System.out.println(""); 12 }
先是直接獲取參數打印出來,而後又將獲取的參數用ISO-8859-1編碼,再用utf-8和GBK分別解碼並打印出來。點擊頁面的Post表單提交,控制檯結果以下:
html提交普通表單時,參數以字節流的方式傳輸到web服務器,web服務器解碼字節流獲得參數。tomcat默認採用ISO-8859-1編解碼,因此直接獲取參數是亂碼,以ISO-8859-1編碼回本來的字節流再用utf-8從新解碼就能夠獲得正確的參數。
爲何tomcat採用ISO-8859-1爲默認的編碼格式,致使解碼流程這麼複雜?在servlet規範中明確規定:若是客戶端請求沒有指定字符編碼,web容器用來建立請求讀取器和解析POST 數據的編碼必須是「ISO-8859-1」。爲何會有這麼奇葩的規定呢?我猜多是適應早期的瀏覽器吧,早期的瀏覽器大多采用ISO-8859-1爲默認編碼,這個編碼的應用是很是普遍的,以爲陌生或難以想象只是由於咱們在中國。稍微捋一下,ASCII碼是1967年的,ISO-8859-1是1987年的,unicode是1994定稿,瀏覽器是在94年末95年初發布,因此估計早期的瀏覽器來不及用unicode做爲默認編碼。我是用這麼一套理論來講服本身的,對不對就不重要了,總之ISO-8859-1在不少國家是廣泛採用的,在人家的圈子裏作東西採用人家的編碼也是正常的。
能夠在獲取任何參數前調用 request.setCharacterEncoding("utf-8") ,便可用utf-8解碼參數,就不用那麼麻煩了。其餘語言在原理上也都是相同的。
實驗二:響應編碼
響應代碼更改成以下代碼。
1 protected void doPost(HttpServletRequest request, 2 HttpServletResponse response) throws ServletException, IOException { 3 // response.setCharacterEncoding("utf-8"); ① 4 // response.setContentType("text/html; charset=UTF-8"); ② 5 // response.setHeader("Content-Type", "text/html; charset=UTF-8"); ③
6 PrintWriter writer = response.getWriter(); 7 writer.print("<html><head><meta charset=\"utf-8\"/></head><body><font color=\"red\">你好</font></body></html>"); 8 writer.close(); 9 }
只在響應的html頁面中指定了頁面編碼,顯示是亂碼,如左下圖所示。由於在輸出時並無指定編碼格式,默認採用ISO-8859-1編碼,瀏覽器用utf-8解碼天然就是亂碼了。
代碼中註釋掉的三行,是java經常使用的設置響應編碼方式。②跟③的效果是同樣同樣的,只討論①和②。將註釋①打開,響應如右下圖所示,指定了響應編碼爲utf-8,瀏覽器用utf-8解碼正確顯示。
setCharacterEncoding只是把響應字符按指定格式編碼,setContentType除此以外,還在header裏添加了contentType屬性。只把註釋②打開也能夠正常顯示,在下圖右側ResponseHeaders中有Content-Type屬性 「text/html; charset=utf-8」 。當header中出現Content-Type屬性並出現指定編碼,瀏覽器將忽略html裏<meta/>標籤中指定的編碼,以header中的編碼解碼html頁面。
當setCharacterEncoding和setContentType同時出現時,後邊的編碼會覆蓋前邊的編碼。響應代碼更改成以下所示。
1 protected void doPost(HttpServletRequest request, 2 HttpServletResponse response) throws ServletException, IOException { 3 response.setContentType("text/html; charset=utf-8"); 4 response.setCharacterEncoding("GBK"); 5 PrintWriter writer = response.getWriter(); 6 writer.print("<html><head><meta charset=\"utf-8\"/></head><body><font color=\"red\">你好</font></body></html>"); 7 writer.close(); 8 }
響應編碼設爲GBK,contentType屬性指定的編碼倒是utf-8,瀏覽器按照contentType指定的編碼來解碼豈不是亂碼?以下圖所示,答案是否認的,真實的contentType是「text/html; charset=GBK」,這是爲何,明明設置的「text/html; charset=utf-8」。由於contentType包含的實際上是兩部分,一部分是text/html,第二部分是charset=utf-8。設置了contentType以後,這兩部分是分別存儲的,setCharacterEncoding會把第二部分編碼的值覆蓋掉。這時contentType就變成了「text/html; charset=GBK」。
實驗四:瀏覽器對動態頁面與靜態頁面在默認編碼上的區別
前邊說過了,html靜態頁面在沒有指定頁面編碼的時候是按照系統默認編碼解碼的。響應代碼更改以下所示。
1 protected void doPost(HttpServletRequest request, 2 HttpServletResponse response) throws ServletException, IOException { 3 response.setCharacterEncoding("utf-8"); 4 PrintWriter writer = response.getWriter(); 5 writer.print("<html><head></head><body><font color=\"red\">你好</font></body></html>"); 6 writer.close(); 7 }
沒有任何地方指定頁面編碼。按照靜態html的規律,瀏覽器採用個人系統默認編碼utf-8解碼,不會出現亂碼。可是實際狀況並非這樣,如左下圖所示,亂碼仍是出現了,由於瀏覽器使用的GBK去解碼。這是爲何呢?打開chrome的設置-》顯示高級設置-》自定義字體-》出現右下圖所示的窗口,能夠看到chrome的默認編碼是GBK,也就說若是動態頁面沒有指定頁面編碼將會按照瀏覽器的默認編碼來解碼。爲了驗證咱們的猜想,將chrome編碼設置爲utf-8,從新請求一遍,果真頁面正確顯示。這只是個人猜想,具體對錯未知,可是未指定頁面編碼的狀況是很少見的,你們在編碼過程當中必定要指定頁面編碼爲頁面實際的存儲編碼。
下篇文章介紹一下應用程序交互時的字節流編碼。