蛋疼的中文编码及其计算机编码历史
更好的博客:http://my.oschina.net/goldenshaw/blog?catalog=536953
一:編碼發展:
1 ANSI編碼:計算機在美國出現,使用單字節8位編碼,共可以表示255個狀態。(0-32(0x00-0x20)表示控制碼),其中0-127可以表示所有的英文字符。稱為ANSI(Ascii American Standard Code for Information Interchange)編碼。
? 不同的國家和地區制定了不同的標準,由此產生了 GB2312、GBK、Big5、Shift_JIS 等各自的編碼標準。這些使用 1 至 4 個字節來代表一個字符的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文Windows操作系統中,ANSI 編碼代表 GBK 編碼;在日文Windows操作系統中,ANSI 編碼代表 Shift_JIS 編碼。 不同 ANSI 編碼之間互不兼容,當信息在國際間交流時,無法將屬于兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。 當然對于ANSI編碼而言,0x00~0x7F之間的字符,依舊是1個字節代表1個字符。這一點是ANSI編碼與Unicode編碼之間最大也最明顯的區別。
?
2 GB2312->GBK->GB18030:ANSI無法滿足非英語國家的使用,故中國制定了一系列的標準,127單字節與ANSI兼容,127以上連續兩個字節,表示一個中文。即程序員口中的:"一個漢字算兩個英文字符!一個漢字算兩個英文字符……"。從GB2312逐漸擴展,字符集逐漸擴展。類似的臺灣繁體標準BIG-5。等等。
?
3 UNICODE(全稱Universal Multiple-Octet Coded Character Set,簡稱UCS,俗稱Unicode):字符編碼之間的轉換相當頭疼,ISO給出解決方案unicode,所有字符必須用雙字節,未來可以擴展UCS-4編碼(4字節)。unicode與GBK等編碼之間不兼容,故需要轉碼表等等。(順便說一下,windows nt底層已經重構使用unicode存儲)
unicode只規定符號的二進制代碼,沒有規定如何存儲和傳輸編碼。會導致計算機無法區分編碼究竟是unicode還是ascii編碼。utf-8是unicode的實現方式之一。
?
4 UTF(UCS Transfer Format?):計算機網絡的興起,字符如何傳輸是個問題。故?utf-8,utf-16分別代表,一次傳輸8位或者16位。網絡傳輸中,計算機架構有的采用低位優先,有些采用高位優先。故傳輸前先發送標識符(FEFF代表高位優先,FFFE代表低位優先)。
UTF-8的編碼規則很簡單,只有二條:
1)對于單字節的符號,字節的第一位設為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。
2)對于n字節的符號(n>1),第一個字節的前n位都設為1,第n+1位設為0,后面字節的前兩位一律設為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。
下表總結了編碼規則,字母x表示可用編碼的位。
Unicode符號范圍 | UTF-8編碼方式
(十六進制) | (二進制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
下面,還是以漢字“嚴”為例,演示如何實現UTF-8編碼。
已知“嚴”的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的范圍內(0000 0800-0000 FFFF),因此“嚴”的UTF-8編碼需要三個字節,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,從“嚴”的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了,“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉換成十六進制就是E4B8A5。
?
5 ?Little endian和Big endian
?Unicode碼可以采用UCS-2格式直接存儲。以漢字”嚴“為例,Unicode碼是4E25,需要用兩個字節存儲,一個字節是4E,另一個字節是25。存儲的時候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
那么很自然的,就會出現一個問題:計算機怎么知道某一個文件到底采用哪一種方式編碼?Unicode規范中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做”零寬度非換行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1。如果一個文本文件的頭兩個字節是FE FF,就表示該文件采用大頭方式;如果頭兩個字節是FF FE,就表示該文件采用小頭方式。
?6 BOM
Byte?order?Mark:UTF-8以字節為編碼單元,沒有字節序的問題。UTF-16以兩個字節為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節序。例如“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節流“594E”,那么這是“奎”還是“乙”?
在UCS編碼中有一個叫做"ZERO?WIDTH?NO-BREAK?SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應該出現在實際傳輸中。UCS規范建議我們在傳輸字節流前,先傳輸字符"ZERO?WIDTH?NO-BREAK?SPACE"。
這樣如果接收者收到FEFF,就表明這個字節流是Big-Endian的;如果收到FFFE,就表明這個字節流是Little-Endian的。因此字符"ZERO?WIDTH?NO-BREAK?SPACE"又被稱作BOM
UTF-8不需要BOM來表明字節順序,但可以用BOM來表明編碼方式。字符"ZERO?WIDTH?NO-BREAK?SPACE"的UTF-8編碼是EF?BB?BF(讀者可以用我們前面介紹的編碼方法驗證一下)。所以如果接收者收到以EF?BB?BF開頭的字節流,就知道這是UTF-8編碼了。
Windows就是使用BOM來標記文本文件的編碼方式的。
?
7 escape加密/unescape解密,%u編碼:
有時候會遇到%u編碼:js通過escape函數,將字符使用utf-16be編碼后,前面加入%u。已經被逐漸廢棄。具體參考這里:http://blog.csdn.net/lvxiangan/article/details/8151670,內容如下:
?
一般來說,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編碼”成為了一個混亂的領域。
下面就讓我們看看,“URL編碼”到底有多混亂。我會依次分析四種不同的情況,在每一種情況中,瀏覽器的URL編碼方法都不一樣。把它們的差異解釋 清楚之后,我再說如何用Javascript找到一個統一的編碼方法。
二、情況1:網址路徑中包含漢字
打開IE(我用的是8.0版),輸入網址“http://zh.wikipedia.org/wiki/春節?”。 注意,“春節”這兩個字此時是網址路徑的一部分。
查看HTTP請求的頭信息,會發現IE實際查詢的網址是“http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82?”。 也就是說,IE自動將“春節”編碼成了“%E6%98%A5%E8%8A%82”。
我們知道,“春”和“節”的utf-8編碼分別是“E6 98 A5”和“E8 8A 82”,因此,“%E6%98%A5%E8%8A%82”就是按照順序,在每個字節前加上%而得到的。(具體的轉碼方法,請參考我寫的《字符編碼筆記》?。)
在Firefox中測試,也得到了同樣的結果。所以,結論1就是,網址路徑的編碼,用的是utf-8編碼。
三、情況2:查詢字符串包含漢字
在IE中輸入網址“http://www.baidu.com/s?wd=春節?”。注意,“春節”這兩個字此時 屬于查詢字符串,不屬于網址路徑,不要與情況1混淆。
查看HTTP請求的頭信息,會發現IE將“春節”轉化成了一個亂碼。
切換到十六進制方式,才能清楚地看到,“春節”被轉成了“B4 BA BD DA”。
我們知道,“春”和“節”的GB2312編碼(我的操作系統“Windows XP”中文版的默認編碼)分別是“B4 BA”和“BD DA”。因此,IE實際上就是將查詢字符串,以GB2312編碼的格式發送出去。
Firefox的處理方法,略有不同。它發送的HTTP Head是“wd=%B4%BA%BD%DA”。也就是說,同樣采用GB2312編碼,但是在每個字節前加上了%。
所以,結論2就是,查詢字符串的編碼,用的是操作系統的默認編碼。
四、情況3:Get方法生成的URL包含漢字
前面說的是直接輸入網址的情況,但是更常見的情況是,在已打開的網頁上,直接用Get或Post方法發出HTTP請求。
根據臺灣中興大學呂瑞麟老師的試驗?,這時的編碼方法由網頁的編碼決定,也就是由HTML源碼中字符集的設定決定。
<meta http-equiv="Content-Type" content="text/html;charset=xxxx">
如果上面這一行最后的charset是UTF-8,則URL就以UTF-8編碼;如果是GB2312,URL就以GB2312編碼。
舉例來說,百度是GB2312編碼,Google是UTF-8編碼。因此,從它們的搜索框中搜索同一個詞“春節”,生成的查詢字符串是不一樣的。
百度生成的是%B4%BA%BD%DA,這是GB2312編碼。
Google生成的是%E6%98%A5%E8%8A%82,這是UTF-8編碼。
所以,結論3就是,GET和POST方法的編碼,用的是網頁的編碼。
五、情況4:Ajax調用的URL包含漢字
前面三種情況都是由瀏覽器發出HTTP請求,最后一種情況則是由Javascript生成HTTP請求,也就是Ajax調用。還是根據呂瑞麟老師的 文章,在這種情況下,IE和Firefox的處理方式完全不一樣。
舉例來說,有這樣兩行代碼:
url = url + "?q=" +document.myform.elements[0].value; // 假定用戶在表單中提交的值是“春節”這兩個字
http_request.open('GET', url, true);
那么,無論網頁使用什么字符集,IE傳送給服務器的總是“q=%B4%BA%BD%DA”,而Firefox傳送給服務器的總是“q=%E6%98 %A5%E8%8A%82”。也就是說,在Ajax調用中,IE總是采用GB2312編碼(操作系統的默認編碼),而Firefox總是 采用utf-8編碼。這就是我們的結論4。
六、Javascript函數:escape()
好了,到此為止,四種情況都說完了。
假定前面你都看懂了,那么此時你應該會感到很頭痛。因為,實在太混亂了。不同的操作系統、不同的瀏覽器、不同的網頁字符集,將導致完全不同的編碼結 果。如果程序員要把每一種結果都考慮進去,是不是太恐怖了?有沒有辦法,能夠保證客戶端只用一種編碼方法向服務器發出請求?
回答是有的,就是使用Javascript先對URL編碼,然后再向服務器提交,不要給瀏覽器插手的機會。因為Javascript的輸出總是一致 的,所以就保證了服務器得到的數據是格式統一的。
Javascript語言用于編碼的函數,一共有三個,最古老的一個就是escape()。雖然這個函數現在已經不提倡使用了,但是由于歷史原因, 很多地方還在使用它,所以有必要先從它講起。
實際上,escape()不能直接用于URL編碼,它的真正作用是返回一個字符的Unicode編碼值。比如“春節”的返回結果 是%u6625%u8282,也就是說在Unicode字符集中,“春”是第6625個(十六進制)字符,“節”是第8282個(十六進制)字符。
它的具體規則是,除了ASCII字母、數字、標點符號“@ * _ + - . /”以外,對其他所有字符進行編碼。在/u0000到/u00ff之間的符號被轉成%xx的形式,其余符號被轉成%uxxxx的形式。對應的解碼函數是 unescape()。
所以,“Hello World”的escape()編碼就是“Hello%20World”。因為空格的Unicode值是20(十六進制)。
還有兩個地方需要注意。
首先,無論網頁的原始編碼是什么,一旦被Javascript編碼,就都變為unicode字符。也就是說,Javascipt函數的輸入和輸出, 默認都是Unicode字符。這一點對下面兩個函數也適用。
其次,escape()不對“+”編碼。但是我們知道,網頁在提交表單的時候,如果有空格,則會被轉化為+字符。服務器處理數據的時候,會把+號處 理成空格。所以,使用的時候要小心。
七、Javascript函數:encodeURI()
encodeURI()是Javascript中真正用來對URL編碼的函數。
它著眼于對整個URL進行編碼,因此除了常見的符號以外,對其他一些在網址中有特殊含義的符號“; / ? : @ & = + $ , #”,也不進行編碼。編碼后,它輸出符號的utf-8形式,并且在每個字節前加上%。
它對應的解碼函數是decodeURI()。
需要注意的是,它不對單引號'編碼。
八、Javascript函數:encodeURIComponent()
最后一個Javascript編碼函數是encodeURIComponent()。與encodeURI()的區別是,它用于對URL的組成部分 進行個別編碼,而不用于對整個URL進行編碼。
因此,“; / ? : @ & = + $ , #”,這些在encodeURI()中不被編碼的符號,在encodeURIComponent()中統統會被編碼。至于具體的編碼方法,兩者是一樣。
它對應的解碼函數是decodeURIComponent()。
PS1?:
網頁里的form編碼其實不完全取決于網頁編碼,form標記中有一個accept-charset屬性,在非ie瀏覽器種,如果將其賦值(比如 accept-charset="UTF-8"),則表單會按照這個值表示的編碼方式進行提交。
在ie下,我的兼容解決辦法是:
form1.οnsubmit=function(){
document.charset=this.getAttribute('accept-charset');
}
?
轉載于:https://www.cnblogs.com/songxinya/p/4711367.html
總結
以上是生活随笔為你收集整理的蛋疼的中文编码及其计算机编码历史的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ 1300 Door Man(欧拉
- 下一篇: matlab曲线拟合预判,matlab里