轉(zhuǎn)自:https://blog.csdn.net/vgwciro8nu/article/details/60583186 首先來看一下常用的編碼有哪些,截圖自Notepad++。其中ANSI在中國大陸即為GBK(以前是GB2312),最常用的是 GBK 和 UTF8無BOM 編碼格式。后面三個(gè)都是有BOM頭的文本格式,UCS-2即為人們常說的Unicode編碼,又分為大端、小端。 所謂BOM頭(Byte Order Mark)就是文本文件中開始的幾個(gè)并不表示任何字符的字節(jié),用二進(jìn)制編輯器(如bz.exe)就能看到了。 UTF8的BOM頭為 0xEF 0xBB0xBF Unicode大端模式為 0xFE 0xFF Unicode小端模式為 0xFF 0xFE 何為GBK,何為GB2312,與區(qū)位碼有何淵源? 區(qū)位碼是早些年(1980)中國制定的一個(gè)編碼標(biāo)準(zhǔn),如果有玩過小霸王學(xué)習(xí)機(jī)的話,應(yīng)該會(huì)記得有個(gè)叫做“區(qū)位”的輸入法(沒記錯(cuò)的話是按F4選擇)。就是打四個(gè)數(shù)字然后就出來漢字了,什么原理呢。請(qǐng)看下面的區(qū)位碼表,每一個(gè)字符都有對(duì)應(yīng)一個(gè)編號(hào)。其中前兩位為“區(qū)”,后兩位為“位”,中文漢字的編號(hào)區(qū)號(hào)是從16開始的,位號(hào)從1開始。前面的區(qū)號(hào)有一些符號(hào)、數(shù)字、字母、注音符號(hào)(臺(tái))、制表符、日文等等。 而GB2312編碼就是基于區(qū)位碼的,用雙字節(jié)編碼表示中文和中文符號(hào)。一般編碼方式是:0xA0+區(qū)號(hào),0xA0+位號(hào)。如下表中的“安”,區(qū)位號(hào)是1618(十進(jìn)制),那么“安”字的GB2312編碼就是0xA0+16 0xA0+18 也就是 0xB0 0xB2。根據(jù)區(qū)位碼表,GB2312的漢字編碼范圍是0xB0A1~0xF7FE 區(qū)位碼表節(jié)選 可能大家注意到了,區(qū)位碼里有英文和數(shù)字,按道理說是不是也應(yīng)該是雙字節(jié)的呢。而一般情況下,我們見到的英文和數(shù)字是單字節(jié)的,以ASCII編碼,也就是說現(xiàn)代的GBK編碼是兼容ASCII編碼的。比如一個(gè)數(shù)字2,對(duì)應(yīng)的二進(jìn)制是0x32,而不是 0xA30xB2。那么問題來了,0xA3 0xB2 又對(duì)應(yīng)到什么呢?還是2(笑)。注意看了,這里的2跟2是不是有點(diǎn)不太一樣?!確實(shí)是不一樣的。這里的雙字節(jié)2是全角的二,ASCII的2是半角的二,一般輸入法里的切換全角半角就是這里不同。 如果留意過早些年的手機(jī)(功能機(jī)),會(huì)發(fā)現(xiàn)人名中常見的“燊”字是打不出來的。為什么呢?因?yàn)樵缙诘膮^(qū)位碼表里面并沒有這些字,也就是說早期的GB2312也是沒有這些字的。到后來的GBK(1995)才補(bǔ)充了大量的漢字進(jìn)去,當(dāng)然現(xiàn)在的安卓蘋果應(yīng)該都是GBK字庫了。再看看這些補(bǔ)充的漢字的字節(jié)碼 燊 0x9F0xF6 。和前面說到的GB2312不同,有的字的編碼比 0xA0 0xA0 還小,難道新補(bǔ)充的區(qū)位號(hào)還能是負(fù)的??其實(shí)不然,這次的補(bǔ)充只補(bǔ)充了計(jì)算機(jī)編碼表,并沒有補(bǔ)充區(qū)位碼表。也就是說區(qū)位碼表并沒有更新,用區(qū)位碼打字法還是打不出這些字,而網(wǎng)上的反向區(qū)位碼表查詢也只是按照GBK的編碼計(jì)算,并不代表字與區(qū)位號(hào)完全對(duì)應(yīng)。時(shí)代的發(fā)展,區(qū)位碼表早已經(jīng)是進(jìn)入博物館的東西了。 Big5是與GB2312同時(shí)期的一種臺(tái)灣地區(qū)繁體字的編碼格式。后來GBK編碼的制定,把Big5用的繁體字也包含進(jìn)來(但編碼不兼容),還增加了一些其它的中文字符。細(xì)心的朋友可能還會(huì)發(fā)現(xiàn),臺(tái)灣香港用的繁體字(如KTV里的字幕)跟大陸用的繁體字還有點(diǎn)筆畫上的不一樣,其實(shí)這跟編碼無關(guān),是字體的不同,大陸一般用的是宋體楷體黑體,港澳臺(tái)常用的是明體(鳥哥Linux私房菜用的是新細(xì)明體)。GBK總體編碼范圍為0x8140~0xFEFE,首字節(jié)在0x81~0xFE 之間,尾字節(jié)在0x40~0xFE 之間,剔除 xx7F 一條線。詳細(xì)編碼表可以參考這個(gè)列表。微軟Windows安排給GBK的code page(代碼頁)是CP936,所以有時(shí)候看到編碼格式是CP936,其實(shí)就是GBK的意思。2000年和2005年,國家又先后兩次發(fā)布了GB18030編碼標(biāo)準(zhǔn),兼容GBK,新增四字節(jié)的編碼,但比較少見。 同一個(gè)編碼文件里,怎么區(qū)分ASCII和中文編碼呢?從ASCII表我們知道標(biāo)準(zhǔn)ASCII只有128個(gè)字符,0~127即0x00~0x7F(0111 1111)。所以區(qū)分的方法就是,高字節(jié)的最高位為0則為ASCII,為1則為中文。 UTF8編碼與 Unicode編碼 GBK是中國標(biāo)準(zhǔn),只在中國使用,并沒有表示大多數(shù)其它國家的編碼;而各國又陸續(xù)推出各自的編碼標(biāo)準(zhǔn),互不兼容,非常不利于全球化發(fā)展。于是后來國際組織發(fā)行了一個(gè)全球統(tǒng)一編碼表,把全球各國文字都統(tǒng)一在一個(gè)編碼標(biāo)準(zhǔn)里,名為Unicode。很多人都很疑惑,到底UTF8與Unicode兩者有什么關(guān)系?如果要類比的話,UTF8相當(dāng)于GB2312,Unicode相當(dāng)于區(qū)位碼表,不同的是它們之間的編號(hào)范圍和轉(zhuǎn)換公式。那什么是原始的Unicode編碼呢?如果你用過PHP的話,json_encode函數(shù)默認(rèn)會(huì)把中文編碼成為Unicode,比如“首發(fā)于博客園”就會(huì)轉(zhuǎn)碼成“\u9996\u53d1\u4e8e\u535a\u5ba2\u56ed”?梢钥吹矫總(gè)字都變成了 \uXXXX 的形式,這個(gè)就是文字的對(duì)應(yīng)Unicode編碼,\u表示Unicode的意思,網(wǎng)上也有用U+表示unicode,F(xiàn)行的Unicode編碼標(biāo)準(zhǔn)里,絕大多數(shù)程序語言只支持雙字節(jié)。英文字母、標(biāo)點(diǎn)也收納在Unicode編碼中。有興趣的可以在站長工具里嘗試“中文轉(zhuǎn)Unicode”,可以得到你輸入文字的Unicode編碼。 因?yàn)橛⑽淖址踩渴褂秒p字節(jié),存儲(chǔ)成本和流量會(huì)大大地增加,所以Unicode編碼大多數(shù)情況并沒有被原始地使用,而是被轉(zhuǎn)換編碼成UTF8。下表就是其轉(zhuǎn)換公式: 第一種:Unicode從 0x0000 到 0x007F 范圍的,是不是有點(diǎn)熟悉?對(duì),其實(shí)就是標(biāo)準(zhǔn)ASCII碼里面的內(nèi)容,所以直接去掉前面那個(gè)字節(jié) 0x00,使用其第二個(gè)字節(jié)(與ASCII碼相同)作為其編碼,即為單字節(jié)UTF8。 第二種:Unicode從 0x0080 到 0x07FF 范圍的,轉(zhuǎn)換成雙字節(jié)UTF8。 第三種:Unicode從 0x8000 到 0xFFFF 范圍的,轉(zhuǎn)換成三字節(jié)UTF8,一般中文都是在這個(gè)范圍里。 第四種:超過雙字節(jié)的Unicode目前還沒有廣泛支持,僅見emoji表情在此范圍。 例如“博”字的Unicode編碼是\u535a。0x535A在0x0800~0xFFFF之間,所以用3字節(jié)模板 1110yyyy 10yyyyxx 10xxxxxx。將535A寫成二進(jìn)制是:01010011 0101 1010,高八位分別代替y,低八位分別代替x,得到 1110010110001101 10011010,也就是 0xE58D9A,這就是博字的UTF8編碼。 前面提到,GBK的編碼里英文字符有全角和半角之分,全角為GBK的標(biāo)準(zhǔn)編碼過的雙字節(jié)2,半角為ASCII的單字節(jié)2。那現(xiàn)在UTF8是全部用一個(gè)公式,理論上只有半角的2的,怎么支持全角的2呢?哈哈,結(jié)果是Unicode為中國特色的全角英文字符也單獨(dú)分配了編碼,簡單粗暴。比如全角的2的Unicode編碼是 \uFF12,轉(zhuǎn)換到UTF8就是 0xEFBC92。 文章開頭有說到 UCS-2,其實(shí)UCS-2就是原始的雙字節(jié)Unicode編碼,用二進(jìn)制編輯器打開UCS-2大端模式的文本文件,從左往右看,看到的就是每個(gè)字符的Unicode編碼了。至于什么是大端小端,就是字節(jié)的存放順序不同,這一般是嵌入式編程的范疇。 如何區(qū)分一個(gè)文本是無BOM的UTF8還是GBK 前面說到的幾種編碼,其中有的是有BOM頭的,可以直接根據(jù)BOM頭區(qū)分出其編碼。有兩個(gè)是沒有BOM頭的,UTF8和GBK,那么兩者怎么區(qū)分呢?答案是,只能按大量的編碼分析來區(qū)分。目前識(shí)別準(zhǔn)確率很高的有:Notepad++等一些常用的IDE,PHP的mb_系列函數(shù),python的chardet庫及其它語言衍生版如jchardet,jschardet 等(請(qǐng)自行github)。 那么這些庫是怎么區(qū)分這些編碼的呢?那就是詞庫,你會(huì)看到庫的源碼里有大量的數(shù)組,其實(shí)就是對(duì)應(yīng)一個(gè)編碼里的常見詞組編碼組合。同樣的文件字節(jié)流在一個(gè)詞組庫里的匹配程度越高,就越有可能是該編碼,判斷的準(zhǔn)確率就越大。而文件中的中文越少越零散,判斷的準(zhǔn)確率就越低。 關(guān)于ASCII 文中多次提及ASCII編碼,其實(shí)這應(yīng)該是每個(gè)程序員都非常熟悉、認(rèn)真了解的東西。對(duì)于嵌入式開發(fā)的人來說,應(yīng)該能隨時(shí)在字符與ASCII碼中轉(zhuǎn)換,就像十六進(jìn)制與二進(jìn)制之間的轉(zhuǎn)換一樣。標(biāo)準(zhǔn)ASCII是128個(gè),范圍是0x00~0x7F (0000 0000~0111 0000) ,最高位為0。也有一個(gè)擴(kuò)展ASCII碼規(guī)則,把最高位也用上了,變成256個(gè),但是這個(gè)擴(kuò)展標(biāo)準(zhǔn)爭議很大,沒有得到推廣,應(yīng)該以后不會(huì)得到推廣。因?yàn)闊o論是GBK還是UTF8,如果ASCII字符編碼最高位能為1都會(huì)造成混亂無法解析。 以GBK為例,如果ASCII的字符最高位也能是1,那么是應(yīng)該截取一個(gè)解析為ASCII呢?還是截取兩個(gè)解析為中文字符?這根本無法判斷。UTF8也是同理,遇到 0xxx 開頭則截取一個(gè)(即為標(biāo)準(zhǔn)ASCII), 遇到 110x 開頭則截取兩個(gè),遇到1110 開頭則截取三個(gè),如果ASCII包含1開頭的,則無法確定何時(shí)截取多少個(gè)。 在哪里還能一睹擴(kuò)展ASCII的真容呢?其實(shí)很簡單,只要把網(wǎng)頁的meta改成ASCII就行了 <meta charset="ASCII" /> 。又或者瀏覽器的編碼選擇“西方”,即可見到與平常所見不同的亂碼。(截圖為火狐) ———————————————— 版權(quán)聲明:本文為CSDN博主「VGWCIrO8NU」的原創(chuàng)文章,遵循 CC4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/vgwciro8nu/article/details/60583186
|