前端開發涉及到的編碼問題


前言

URL只能使用英文字母、阿拉伯數字和某些標點符號,不能使用其他文字和符號。比如,世界上有英文字母的網址"http://www.abc.com",但是沒有希臘字母的網址"http://www.aβγ.com"(讀作阿爾法-貝塔-伽瑪.com)。這是因為網絡標准RFC 1738做了硬性規定:

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."

"只有字母和數字[0-9a-zA-Z]、一些特殊符號"$-_.+!*'(),"[不包括雙引號]、以及某些保留字,才可以不經過編碼直接用於URL。"

這意味着,如果URL中有漢字,就必須編碼后使用。但是麻煩的是,RFC 1738沒有規定具體的編碼方法,而是交給應用程序(瀏覽器)自己決定。這導致"URL編碼"成為了一個混亂的領域。

如下:

1:地址欄輸入和點擊url訪問瀏覽器時,chrome下無論請求地址和參數,均經過utf-8編碼;非chrome瀏覽器,其中請求地址用utf-8編碼,參數按操作系統編碼進行編碼(ie下的編碼參數部分都不帶%)。但參數中含特殊符號時,未確定何特殊符號會被編碼。

例子:http://www.google.com/小明|=?param=小明|=
a.上面的url在中文xp的chrome下,發送的請求地址是

http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=%E5%B0%8F%E6%98%8E|=

b.在中文xp的firefox下,發送的請求地址是
http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=%D0%A1%C3%F7|= 

c.在中文xp的ie下,發送的請求地址是
http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=С??|=

2:超鏈接中的請求,請求地址用utf-8編碼,參數用頁面編碼,但是在ie下的參數不帶%號。

在上面的例子中,在頁面指定utf-8編碼下,參數部分會變成:param=%E5%B0%8F%E6%98%8E|=。但ie會變成param=E5B08FE6988E|=。

3:js進行超鏈接訪問時,和超鏈接情況一致。

4:ajax異步請求時,ie總是用gbk來編碼,包括網頁路徑和參數,非ie和超鏈接請求一致。

在上面例子中,ie下發送的請求會變成:http://www.google.com/%D0%A1%C3%F7%7C=?param=d0a1c3f7|=

 

form提交涉及到的編碼

 form 表單中有一個 accept-charset  屬性,如果沒有此屬性會用頁面編碼來提交,如果頁面編碼沒有就是 utf-8編碼。

 

兩個應用編碼不同,一個是GBK編碼,另一個是UTF-8編碼。現在要在GBK編碼的應用里使用表單向UTF-8編碼的應用里提交數據,很顯然,如果不做特殊處理的話,會出現亂碼。 

解決方案:
在GBK編碼的頁面里編寫如下代碼: 
<form method="post" action="..." accept-charset="utf-8"> ... </form> 

如此的代碼在Firefox等正常的瀏覽器下沒有任何問題,但是遇到IE這個變態瀏覽器就不靈光了,我們還得用點不入流的手段Hack一下: 
<form method="post" action="..." accept-charset="utf-8" onsubmit="document.charset='utf-8';"> ... </form> 

 

 

ajax 中涉及到的編碼

ajax 提交到服務器的編碼上面已經討論過。但是獲取后台內容也會有亂碼的情況出現:

如果ajax 后台返回的不是utf-8的編碼的二進制流,那么在xmlhttprequest 對象的responseText 方法中字符就會亂碼,因為瀏覽器默認用utf-8來解碼。 

我在后台返回時的conent-type 中也明確書寫了charset :gbk 。但是實測沒起到作用。 這個等有時間在驗證

 

注意:

無論何種方式提交的服務器,瀏覽器都會轉化成ascll字符,然后以ascll對應的碼點轉成二進制進行傳輸。對於那么不能直接轉成ascll字符的,比如漢字。也會先用以上的編碼方式轉成ascll字符。
比如:
春節 ==》%E6%98%A5%E8%8A%82  utf-8編碼
       ==》 %B4%BA%BD%DA            gbk 編碼

在utf-8編碼下,一個漢字 會有9個字節進行傳輸。

在一個html標簽的屬性中會有長度的限制。比如
a 標簽的href 的長度限制,計算這個長度時要注意以上的細節

 

由於各個瀏覽器處理起來的不一致,所以我們需要用js來處理編碼,因為js的處理結果都是一致的,都是utf-8編碼。三個函數:

1.escape()

雖然這個函數現在已經不提倡使用了,但是由於歷史原因,很多地方還在使用它,所以有必要先從它講起。

實際上,escape()不能直接用於URL編碼,它的真正作用是返回一個字符的Unicode編碼值。比如"春節"的返回結果是%u6625%u8282,也就是說在Unicode字符集中,""是第6625個(十六進制)字符,""是第8282個(十六進制)字符。

  它的具體規則是,除了ASCII字母、數字、標點符號"@ * _ + - . /"以外,對其他所有字符進行編碼。在\u0000到\u00ff之間的符號被轉成%xx的形式,其余符號被轉成%uxxxx的形式。對應的解碼函數是unescape()。

 escape對0-255以外的unicode值進行編碼時輸出%u****格式,其它情況下escape,encodeURI,encodeURIComponent編碼結果相同。

  所以,"Hello World"的escape()編碼就是"Hello%20World"。因為空格的Unicode值是20(十六進制)

  首先,無論網頁的原始編碼是什么,一旦被Javascript編碼,就都變為unicode字符。也就是說,Javascipt函數的輸入和輸出,默認都是Unicode字符。這一點對下面兩個函數也適用。

  其次,escape()不對"+"編碼。但是我們知道,網頁在提交表單的時候,如果有空格,則會被轉化為+字符。服務器處理數據的時候,會把+號處理成空格。所以,使用的時候要小心。可以這樣處理下 str.replace(/+/g,"%u002B");

  escape不編碼字符有69個:*,+,-,.,/,@,_,0-9,a-z,A-Z

 

2.encodeURI()

encodeURI()是Javascript中真正用來對URL編碼的函數。
它着眼於對整個URL進行編碼,因此除了常見的符號以外,對其他一些在網址中有特殊含義的符號"; / ? : @ & = + $ , #",也不進行編碼。編碼后,它輸出符號的utf-8形式,並且在每個字節前加上%。
它對應的解碼函數是decodeURI()。 需要注意的是,它不對單引號'編碼。

 

3.encodeURIComponent()

最后一個Javascript編碼函數是encodeURIComponent()。與encodeURI()的區別是,它用於對URL的組成部分進行個別編碼,而不用於對整個URL進行編碼。
因此,"; / ? : @ & = + $ , #",這些在encodeURI()中不被編碼的符號,在encodeURIComponent()中統統會被編碼。至於具體的編碼方法,兩者是一樣。
它對應的解碼函數是decodeURIComponent()。
encodeURIComponent不編碼字符有71個:!, ',(,),*,-,.,_,~,0-9,a-z,A-Z

 

上面說到form post提交的時候可以設置 charset。那ajax提交的時候怎么辦?比如怎么把上面三個函數處理過的utf-8編碼的字符發送到GBK編碼的后台

首先,當我們使用提交時,瀏覽器會根據當前頁面編碼,encode一次,然后發送到服務端,服務端接收到表單,會自動dencode一次,通常這個過程是對程序是透明的。即,瀏覽器把請求翻譯成ascll字符傳輸,服務端接受二進制並翻譯成ascll進行dencode。

這個dencode 和前端的encode 的編碼不一致問題就來了。

解決:

1.使用escape函數 把中文換成%uxxxx的形式提交。這樣瀏覽器encode的時候不會在處理了。到了后台,dencode完也是這種形式。 之后再用utf-8轉一下。

2.

1.在A應用處用encodeURIComponent()2次編碼參數數據,如k=中文(utf-8編碼為%E4%B8%AD%E6%96%87),進行2次編碼后,encodeURIComponent(encodeURIComponent(k))=%25E4%25B8%25AD%25E6%2596%2587

2.將請求發送到url2 = http://www.xxx.com/a.htm?k=%25E4%25B8%25AD%25E6%2596%2587&encoding=utf-8

3.url2對應應用收到這個請求后,web容器會對%25進行解碼,變成%,對應request.getParameter("k")=%E4%B8%AD%E6%96%87

4.再URLDecoder.decode("%E4%B8%AD%E6%96%87", "UTF-8")就能解碼回原來的中文而不會亂碼了。

 

 

 

 

從編碼到頁面呈現中涉及的編碼

我們在開發過程中新建文件(js,css,html),都會有默認的編碼方式存儲在硬盤上。不過的開發工具新建文件時的編碼方式不同。

源代碼發布到web容器后,用戶在瀏覽器訪問我們的文件,web容器以二進制流發送我們的文件給瀏覽器。

瀏覽器要解析這個二進制流。那么問題來了,瀏覽器用什么編碼去解析?

1.根據http協議中的 content-type 中的charset ,content-type中的charset的優先級最高,如果有charset選項瀏覽器將忽略以下規則

2.html頁面中meta標簽中的charset。(js,css 也有charset ,如果有將用此charset ,如果沒有就用宿主html的charset)

3.瀏覽器默認charset 

瀏覽器解析完這個二進制流之后,會把這些信息存在變量中。 變量在內存中存儲的編碼方式就是瀏覽器默認的了,跟以上的規則都沒有關系了。 包括在html input 中輸入字符都是瀏覽器以默認編碼存儲在內存中。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM