首先來看一下經常使用的編碼有哪些,截圖自Notepad++。其中ANSI在中國大陸即爲GBK(之前是GB2312),最經常使用的是 GBK 和 UTF8無BOM 編碼格式。後面三個都是有BOM頭的文本格式,UCS-2即爲人們常說的Unicode編碼,又分爲大端、小端。php
所謂BOM頭(Byte Order Mark)就是文本文件中開始的幾個並不表示任何字符的字節,用二進制編輯器(如bz.exe)就能看到了。html
區位碼是早些年(1980)中國製定的一個編碼標準,若是有玩太小霸王學習機的話,應該會記得有個叫作「區位」的輸入法(沒記錯的話是按F4選擇)。就是打四個數字而後就出來漢字了,什麼原理呢。請看下面的區位碼錶,每個字符都有對應一個編號。其中前兩位爲「區」,後兩位爲「位」,中文漢字的編號區號是從16開始的,位號從1開始。前面的區號有一些符號、數字、字母、注音符號(臺)、製表符、日文等等。python
而GB2312編碼就是基於區位碼的,用雙字節編碼表示中文和中文符號。通常編碼方式是:0xA0+區號,0xA0+位號。以下表中的 「安」,區位號是1618(十進制),那麼「安」字的GB2312編碼就是 0xA0+16 0xA0+18 也就是 0xB0 0xB2 。根據區位碼錶,GB2312的漢字編碼範圍是0xB0A1~0xF7FElinux
區位碼錶節選git
可能你們注意到了,區位碼裏有英文和數字,按道理說是否是也應該是雙字節的呢。而通常狀況下,咱們見到的英文和數字是單字節的,以ASCII編碼,也就是說現代的GBK編碼是兼容ASCII編碼的。好比一個數字2,對應的二進制是0x32,而不是 0xA3 0xB2。那麼問題來了,0xA3 0xB2 又對應到什麼呢?仍是2(笑)。注意看了,這裏的2跟2是否是有點不太同樣?!確實是不同的。這裏的雙字節2是全角的二,ASCII的2是半角的二,通常輸入法裏的切換全角半角就是這裏不一樣。程序員
若是留意過早些年的手機(功能機),會發現人名中常見的「燊」字是打不出來的。爲何呢?由於早期的區位碼錶裏面並無這些字,也就是說早期的GB2312也是沒有這些字的。到後來的GBK(1995)才補充了大量的漢字進去,固然如今的安卓蘋果應該都是GBK字庫了。再看看這些補充的漢字的字節碼 燊 0x9F 0xF6 。和前面說到的GB2312不一樣,有的字的編碼比 0xA0 0xA0 還小,難道新補充的區位號還能是負的??其實否則,此次的補充只補充了計算機編碼表,並無補充區位碼錶。也就是說區位碼錶並無更新,用區位碼打字法仍是打不出這些字,而網上的反向區位碼錶查詢也只是按照GBK的編碼計算,並不表明字與區位號徹底對應。時代的發展,區位碼錶早已是進入博物館的東西了。github
Big5是與GB2312同時期的一種臺灣地區繁體字的編碼格式。後來GBK編碼的制定,把Big5用的繁體字也包含進來(但編碼不兼容),還增長了一些其它的中文字符。細心的朋友可能還會發現,臺灣香港用的繁體字(如KTV裏的字幕)跟大陸用的繁體字還有點筆畫上的不同,其實這跟編碼無關,是字體的不一樣,大陸通常用的是宋體楷體黑體,港澳臺經常使用的是明體(鳥哥Linux私房菜用的是新細明體)。GBK整體編碼範圍爲0x8140~0xFEFE,首字節在 0x81~0xFE 之間,尾字節在 0x40~0xFE 之間,剔除 xx7F 一條線。詳細編碼表能夠參考這個列表。微軟Windows安排給GBK的code page(代碼頁)是CP936,因此有時候看到編碼格式是CP936,其實就是GBK的意思。2000年和2005年,國家又前後兩次發佈了GB18030編碼標準,兼容GBK,新增四字節的編碼,但比較少見。編程
同一個編碼文件裏,怎麼區分ASCII和中文編碼呢?從ASCII表咱們知道標準ASCII只有128個字符,0~127即0x00~0x7F(0111 1111)。因此區分的方法就是,高字節的最高位爲0則爲ASCII,爲1則爲中文。json
GBK是中國標準,只在中國使用,並無表示大多數其它國家的編碼;而各國又陸續推出各自的編碼標準,互不兼容,很是不利於全球化發展。因而後來國際組織發行了一個全球統一編碼表,把全球各國文字都統一在一個編碼標準裏,名爲Unicode。不少人都很疑惑,到底UTF8與Unicode二者有什麼關係?若是要類比的話,UTF8至關於GB2312,Unicode至關於區位碼錶,不一樣的是它們之間的編號範圍和轉換公式。那什麼是原始的Unicode編碼呢?若是你用過PHP的話,json_encode函數默認會把中文編碼成爲Unicode,好比「首發於博客園」就會轉碼成「\u9996\u53d1\u4e8e\u535a\u5ba2\u56ed」。能夠看到每一個字都變成了 \uXXXX 的形式,這個就是文字的對應Unicode編碼,\u表示Unicode的意思,網上也有用U+表示unicode。現行的Unicode編碼標準裏,絕大多數程序語言只支持雙字節。英文字母、標點也收納在Unicode編碼中。有興趣的能夠在站長工具裏嘗試「中文轉Unicode」,能夠獲得你輸入文字的Unicode編碼。數組
由於英文字符也所有使用雙字節,存儲成本和流量會大大地增長,因此Unicode編碼大多數狀況並無被原始地使用,而是被轉換編碼成UTF8。下表就是其轉換公式:
第一種:Unicode從 0x0000 到 0x007F 範圍的,是否是有點熟悉?對,其實就是標準ASCII碼裏面的內容,因此直接去掉前面那個字節 0x00,使用其第二個字節(與ASCII碼相同)做爲其編碼,即爲單字節UTF8。
第二種:Unicode從 0x0080 到 0x07FF 範圍的,轉換成雙字節UTF8。
第三種:Unicode從 0x8000 到 0xFFFF 範圍的,轉換成三字節UTF8,通常中文都是在這個範圍裏。
第四種:超過雙字節的Unicode目前尚未普遍支持,僅見emoji表情在此範圍。
例如「博」字的Unicode編碼是\u535a。0x535A在0x0800~0xFFFF之間,因此用3字節模板 1110yyyy 10yyyyxx 10xxxxxx。將535A寫成二進制是:0101 0011 0101 1010,高八位分別代替y,低八位分別代替x,獲得 11100101 10001101 10011010,也就是 0xE58D9A ,這就是博字的UTF8編碼。
前面提到,GBK的編碼裏英文字符有全角和半角之分,全角爲GBK的標準編碼過的雙字節2,半角爲ASCII的單字節2。那如今UTF8是所有用一個公式,理論上只有半角的2的,怎麼支持全角的2呢?哈哈,結果是Unicode爲中國特點的全角英文字符也單獨分配了編碼,簡單粗暴。好比全角的2的Unicode編碼是 \uFF12,轉換到UTF8就是 0xEFBC92。
文章開頭有說到 UCS-2,其實UCS-2就是原始的雙字節Unicode編碼,用二進制編輯器打開UCS-2大端模式的文本文件,從左往右看,看到的就是每一個字符的Unicode編碼了。至於什麼是大端小端,就是字節的存放順序不一樣,這通常是嵌入式編程的範疇。
前面說到的幾種編碼,其中有的是有BOM頭的,能夠直接根據BOM頭區分出其編碼。有兩個是沒有BOM頭的,UTF8和GBK,那麼二者怎麼區分呢?答案是,只能按大量的編碼分析來區分。目前識別準確率很高的有:Notepad++等一些經常使用的IDE,PHP的mb_系列函數,python的chardet庫及其它語言衍生版如jchardet,jschardet 等(請自行github)。
那麼這些庫是怎麼區分這些編碼的呢?那就是詞庫,你會看到庫的源碼裏有大量的數組,其實就是對應一個編碼裏的常見詞組編碼組合。一樣的文件字節流在一個詞組庫裏的匹配程度越高,就越有多是該編碼,判斷的準確率就越大。而文件中的中文越少越零散,判斷的準確率就越低。
文中屢次說起ASCII編碼,其實這應該是每一個程序員都很是熟悉、認真瞭解的東西。對於嵌入式開發的人來講,應該能隨時在字符與ASCII碼中轉換,就像十六進制與二進制之間的轉換同樣。標準ASCII是128個,範圍是0x00~0x7F (0000 0000~0111 0000) ,最高位爲0。也有一個擴展ASCII碼規則,把最高位也用上了,變成256個,可是這個擴展標準爭議很大,沒有獲得推廣,應該之後不會獲得推廣。由於不管是GBK仍是UTF8,若是ASCII字符編碼最高位能爲1都會形成混亂沒法解析。
以GBK爲例,若是ASCII的字符最高位也能是1,那麼是應該截取一個解析爲ASCII呢?仍是截取兩個解析爲中文字符?這根本沒法判斷。UTF8也是同理,遇到 0xxx 開頭則截取一個(即爲標準ASCII), 遇到 110x 開頭則截取兩個,遇到 1110 開頭則截取三個,若是ASCII包含1開頭的,則沒法肯定什麼時候截取多少個。
在哪裏還能一睹擴展ASCII的真容呢?其實很簡單,只要把網頁的meta改爲ASCII就好了 <meta charset="ASCII" /> 。又或者瀏覽器的編碼選擇「西方」,便可見到與日常所見不一樣的亂碼。(截圖爲火狐)
轉自於https://www.cnblogs.com/hehheai/p/6510879.html