前端歷史課:那些來自洪荒時代的編碼知識

作者:伯樂在線/skiner如有好文章投稿,請點擊 → 這裡了解詳情何為編碼? 這可能要從一個勵志的文字遊戲說起。規則: 規定 A,B 兩人之間只能用數字進行溝通, 把 「我愛你」 傳遞給對方。於是 A 想出了一個主意,他去書店買了兩本一樣的新華字典,然後把 「我」 「愛」 「你」 三個字查出來, 假設分別是在第 672 頁的第 3 個字, 和第 102 頁的第 6 個字, 和第 378 頁的第 1 個字。於是 A 將這些頁碼信息寫在一起, 即:672310263781B 接到了這一串數字, 然後通過 A 送的字典, 一個個的將這些字還原了回來, B 感動極了。於是拒絕了 A。計算機, 數字, 字眾所周知,計算機中只能流轉數字, 那我們看到的文字又是怎麼顯示出來的呢?通過上面的故事, 我想大家已經找到答案了。其實在計算機內都有一本虛擬的 「字典」 它記錄著 「數字」 與 「自然字」 之間的映射關係。那麼這本 「字典」 是怎麼做出來的呢?早在 196x 年 美帝幾個任性的科學家決定用 8 bit (1B) 來表示一個 「自然字」 , 當然 1B 的容量限制了這些字的種類數量不能超過 2 的 8 次方(256)個。 不過好在英文字母一共就 26 個。大小寫外加標點算起來一共也就百來個(小於 128 個, 其實 7 bit 就足夠了), 所以就當時的情況來說, 1B 的空間是絕對夠用的, 就這樣 256 個空間中的的前 128 個很快就被分配完畢了, 他們的映射關係是:0 – 31 以及 127 分給了一些有特殊身份的功能性」字元」(如換行 換頁 震動 移至行首 等等)32 – 126 分給了字元(其中 48-57 為數字, 65-90 大寫, 97-122 小寫,其餘標點)這套映射關係就是後來一直延續到今天的 ASCII 碼。(American Standard Code for Information Interchange 美國信息交換標準代碼)標準 ASCII 碼是國際標準, 它只佔用了 1 個位元組的前 7 位,並預留一位做擴展,或作奇偶校驗。這裡有一份 標準ASCII表 可以查詢詳細的對應關係 , 當然 linux 下也可以 man 一下 ascii。

(標準 ASCII 表)

(ASCII 擴展表)奇偶校驗:簡單來說, 奇偶校驗是一種檢測數據傳輸時正確性的方法,它分為 奇校驗 和 偶校驗兩種, 分別檢測二進位數據中 「1」 的個數是奇數或偶數。 並將校驗結果位追加到數據之後。1。奇校驗:如果數據中的 「1」 的個數是奇數,那麼校驗位為 0 否則為 1。例如: 1001000 => 奇校驗 1001000+1 , 1001001 => 奇校驗 1001001+02。偶校驗:如果數據中的 「1」 的個數是偶數,那麼校驗位為 0 否則為 1。例如: 1001000 => 偶校驗 1001000+0 , 1001101 => 偶校驗 1001101+1奇偶校驗只能檢查錯誤,但並不能糾正錯誤。各國間混亂的編碼過了一段時間,人們發現這 128 個字的不夠用了,於是將手伸向了 127 號後面的區域。前文說了,ASCII 中只有前7位(128個字)是國際標準, 而後邊 128 個坑並沒有做出規定或限制, 也沒有留下任何使用說明書。於是,輪到各個國家與公司開腦洞的時間了。這後 128 個坑位稱為 ASCII 擴展碼, 它不是國際標準。也不可能是國際標準,因為每個國家都有自己的需求,但坑位又只有 128 個, 不來一場世界大戰才怪。 其中用的比較廣的可能要算是 IBM擴展字符集 了,也就是 windows 使用的 cp437 字元編碼。DBCS 雙位元組字符集計算機進入我國的時間比較晚, 但漢字作為一種非拼音文字卻是一個非常難處理問題, 已知漢字總共有 8 萬多個, 即便常用的也有 3500 個。 這對於編碼空間來講是一個巨大的考驗。這個難題我想一定是東北人解決的。 因為。東北第一定律: 「世界上沒有什麼事是一頓燒烤不能解決的。如果有,那就兩頓」

如果一個 ASCII 不能解決, 那就兩個 !!!實踐結果證明這種做法確實行之有效,並一直保留到了今天。為了深入了解雙位元組字符集的概念,下面我們就舉例說說我國的這幾個常見編碼集。GB 系列: GB2312、GB13000、 GBK、GB18030GB2312:GB2312 是最早(1980年)發布的中國國家標準字符集 ,是一個典型的雙位元組字符集(DBCS)。 他規定了兩個連續大於 127 的位元組代表一個漢字, 否則就當做 ASCII 來處理, 這樣的兩位元組中高位位元組範圍 0xA1-0xF7, 低位位元組範圍 0xA1-0xFE。 二者相乘多出來 8000 多個坑位, 已經足夠容納常用漢字了, 除此之外還增加了數學符號、羅馬希臘字母、日文假名等。 這種規則完全兼容 ASCII , 但為了更好的兼容 CP437 也做了很多空間犧牲, 比如 GB2312 的低位也被要求大於 127, 所以 GB2312 的編碼空間並不大。不幸的是當時我國一位重要領導人的名字中, 有一個字是 GB2312 里沒有的。 (只能用 釒容)漢卡:

早期的計算機運算能力非常弱, 而 GB2312 的編碼規則無形中又給解碼增加了額外負擔, 所以為了提高漢字的輸入性能, 一種叫 「漢卡」 的硬體出現了, 他直接負責 GB2312 的編碼解碼。 為 CPU 分擔任務。但作為一款硬體, 它的流行又使得 GB13000 推行不下去。GB13000:總結了 GB2312 的種種缺陷, 我國國家標準化委員會又制定了 GB13000, 雖然它也是雙位元組字符集,但與 GB2312 完全不同,他參照了 Unicode 標準, 也就是說 GB13000 是面向國際化的, 但這也直接導致了他不兼容 GB2312 。 真金白銀買的漢卡沒法用了。 這怎麼行 ?所以 GB13000 淪為了 「紙面上的標準」。至於 GB13000 的編碼規則這裡就不做介紹了, 可以直接參考下文 unicode。GBK:鑒於我國漢卡的流行情況, 我國有關部門又制定了 GBK 標準,它兼容 GB2312, 同時又新增了 2w 多個坑位。它是怎麼做到的呢?GBK 放開了一些範圍, 相比 GB2312 , GBK 的高位位元組擴展到了 0×81-0xFE, 低位位元組擴展到了 0x40-0xFE (不包含0x7F) 編碼方式和 GB2312 相同。由此可見 GBK 是 GB2312 的一個超集, 且和子集兼容良好。GB18030:GB18030 是目前我國最新的內碼字集, 它和其他 GB 開頭的小夥伴最大的區別就是,它是變長的編碼集。所謂變長是指一個 GB18030 字有可能由 1 個、2 個或 4 個位元組組成。 與 UTF 系列類似。它與 GBK , GB2312 , ASCII 都是兼容的。1 位元組的值範圍: 0 到 0x7F,與 ASCII 兼容。2 位元組的值範圍: 高位 0x81 到 0xFE。 低位 0x40 到 0xFE 與 GBK 標準兼容。4 位元組的值範圍: 一位元組 0x81 到 0xFE,二位元組 0x30 到 0x39,三位元組 0x81 到 0xFE,四位元組從 0x30 到 0x39GB18030 可容納的編碼範圍巨大, 基本上亞洲所有的少數民族的文字都包含在內了。可以看出在這 GB 幾兄弟中 GB2312 => GBK => GB18030 被依次兼容。走向世界上文中的 GB 幾兄弟 雖然很好的解決的漢字編碼問題,但卻不能走向國際舞台,因為無論 GB2312 還是 GBK 它們都是一種 「地區性DBCS」。 這導致了一個非常麻煩的問題,就是同一個文本文件可能因為編碼集問題而在國外計算機中根本無法閱讀。看來這個世界需要一個統一的字符集。於是 ISO (國際標誰化組織) 開始著手解決這個問題。 它發起了 ISO 10646 項目, 名為 「Universal Multiple Octet Coded Character Set」, 簡稱 UCS。 方案中廢棄了所有地方性的 DBCS 方案,並嘗試把它們融合在一個 DBCS 方案中。同時 統一碼聯盟 Unicode (由各個大型企業及組織共同維護) 發布了統一碼(Unicode)項目。起初,UCS 和 unicode 並不兼容, 但這倆項目本身就是為了解決衝突而生,搞出兩套有何意義? 後來 unicode 便與 UCS 統一了。目前的 unicode 普遍採用的是 UCS-2地方性 DBCS 存在一個共有的問題,就是它們為了兼容 ASCII , 0-127 是不能當做高位來使用的。 這使得一大塊編碼區域被浪費掉, unicode 為了解放這一區域,規定所有字元都必須用兩位元組來表示(即使是 ASCII 字元),這樣這種 DBCS 的容量就被大大擴充了。在這種規則下 2 位元組共可以產生 65536 個坑位。 不同國家的字元分別佔據這 6w+ 坑位中的某一段。 如 希臘字母表使用從0x0370到0x03FF的代碼,斯拉夫語使用從0x0400到0x04FF的代碼, 美國使用從0x0530到0x058F的代碼,希伯來語使用從0x0590到0x05FF的代碼。中國、日本和韓國(總稱為CJK)使用從0x3000到0x9FFF的代碼(還是我們占的比較多 (⊙﹏⊙)b)。這些坑位目前還有一些空餘,一般使用 FFFD 佔據,但在不同軟體或操作系統中可能會略有不同。比如 這個字

(u3237) 在 sublime Text 中會顯示成

節約光榮,浪費可恥雖然 unicode 幫我們解決了字符集統一的問題,但這並不是沒代價的。 在 unicode 下所有的字元都是用 2(UCS2) 或 4(UCS4) 個位元組來編碼,甚至是 ASCII 字元。 這樣便造成了大量浪費,尤其是在進行網路傳輸的時候。 (當你走進聯通營業廳後會發現, 流量啊 流量。 你可是真金白銀啊。)為了解決這一問題, 科學家們定義了一種基於 unicode 的便於傳輸的編碼格式 UTF(UCS Transfer Format), UTF 也有 UTF-8 UTF-16 UTF-32 這幾個兄弟 橫杠後的數字代表嘗試將單個 unicode 壓縮成幾 bit。比如 UTF-8 它就將嘗試用 8 bit 來表示一個 unicode 字。那麼用 8 bit(1位元組) 來表示 16bit(2位元組)。 肯定會遇到撐不下的情況,怎麼辦?Unicode – UTF8十六進位二進位0000 0000-0000 007F0xxxxxxx0000 0080-0000 07FF110xxxxx 10xxxxxx0000 0800-0000 FFFF1110xxxx 10xxxxxx 10xxxxxx0001 0000-0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx上圖中的 x 為 UTF-8 的可編碼範圍。它的轉換規則如下:1。 如果某位元組第一位是 0 ,那麼判定為 ASCII 位元組,除了 0 外餘下的 7 位是 ASCII 碼。所以 UTF-8 是兼容 ASCII 碼的。 2。 如果第一個位元組是 1 , 那麼連續的幾個 「1」 代表了從這個字元開始,後面連續的幾個字元其實是一個字位。 且後面的位置都要是用 10 開頭的。上圖,上圖,上圖

UTF-16 與上面類似。它嘗試用 2 位元組來表示一個 unicode 字,在 UTF-16 下如果表示 UCS2 那麼 2 位元組基本足矣。 所以它主要是用來壓縮 UCS4 的 當 2 位元組搞不定時,就會嘗試使用更多位元組,實現原理和 UTF-8 類似。現在 windows 下的 unicode 實現大多使用的都是 utf-16漢字的窘境到目前的為止, unicode 默認採用的是 UCS2, 而傳輸則是 UTF-8, 對於 ASCII 字元, 在 UTF-8 下體積仍然是 1 位元組。 但是漢字由於編碼段比較靠後, 用 UTF-8 就比較吃虧,基本都需要用 3 個 或 4 個位元組來表示。這反而變得比 unicode 本身還大。所以。 對於 ASCII 字比較多的文章, UTF-8 壓縮效率明顯, 基本可以降低一半體積。 但是對於漢字較多的文章, UTF-8 反而不如 unicode。windows 下的 ANSIwindows 下常見的是一種叫 ANSI 的編碼。 但其實 ANSI 並不是一種「編碼」 ,ANSI 會根據操作系統系統所在的地域,自動指定為一種地域性的 DBCS , 如在簡體中文的 windows 下 ANSI 其實就是 GBK, windows 默認使用 ANSI , 比如你建一個記事本文件,在裡面的字默認會是 ANSI 的。u"我".encode("unicode_escape")# b"\u6211"u"我".encode("gbk")# b"xcexd2"「我」 字的 unicode 是 62 11 GBK 下是 ce d2我們來建一個記事本里,面寫一個 「我」

然後我們來查看他的位元組碼。

可以看到 ce d2 就是 「我」 的 GBK碼。錕斤拷錕斤拷燙燙燙燙。有詩為證:手持兩把錕斤拷,口中疾呼燙燙燙。腳踏千朵屯屯屯,笑看萬物鍩鍩鍩。其中最出名的, 應該算是 「錕斤拷」 和 「燙」 了。這些經典的亂碼是怎麼出現的呢?首先來解釋 「錕斤拷」從釒字旁就能看出質地, 拷在中國古代乃是一種兵器, 話說當年女媧補天時 太上老。。(啪~)

「錕斤拷」 主要出現在 GBK 和 Unicode 混用的網頁中。在比較老的 web 伺服器上是重災區。想打造出 「錕斤拷」 需要三位仙人配合: html文件,web server 和 瀏覽器。製造流程是, 你的 html文件是 GBK 的而 web server 把它強行用 unicode 解碼,解碼後發現味兒不對, 將轉碼失敗的字全部用 「U+FFFD」 代替,然後將「U+FFFD」 轉成 utf-8 後又發給了瀏覽器。「U+FFFD」轉為 utf-8 後為 xEFxBFxBD , 如果是兩個連續的 「U+FFFD」那結果就是 xEFxBFxBDxEFxBFxBD最後你的瀏覽器用 GBK 解碼 xEFxBFxBDxEFxBFxBD 你便得到了神器」錕斤拷」附一個 python 「錕斤拷」的例子錕(0xEFBF)斤(0xBDEF)拷(0xBFBD)ps: 這裡 「U+FFFD」乃是 unicode 的一個佔位符, 顯示為 ? , 當 unicode 遇到解釋失敗的字時,會嘗試用 「U+FFFD」 來代替。下面說說 「燙」「燙」 主要出沒於 windows 平台下,ms 的 vc++ 編譯器中, 當你在棧內開闢新內存時, vc 會使用 0xcc 來初始化填充, 很多個 0xcc 連起來就成了 燙燙燙燙燙 同理在堆內開闢新內存時, 會用 0xcd 填充,這便是 屯屯屯屯屯屯 詳見這裡不管是 「錕斤拷」 還是 「燙」 都要求最後是用 GB 碼輸出。了解 escape 與 encodeURI ,encodeURIComponent 的編碼格式差異javascript 中 String 原型里提供了一個內置函數 String.prototype.charCodeAt 它返回 string 中某個位置上的 unicode 十進位碼值。varjiong="囧".charCodeAt(0);// 22247varjiongHex=jiong.toString(16);// "56e7"escape:escape 不轉碼的字元包括 *,+,-,。,/,@,_,0-9,a-z,A-Z ,他們會以原樣輸出。遇到非上述字元時,它會返回 unicode 碼。 比如 escape(「囧」) 返回 「%u56E7″其中 「56E7」 就是 「囧」 的 unicode 碼。encodeURI:encodeURI 不轉碼的字元包括 !,#,$,&,』,(,),*,+,,,-,。,/,:,;,=,?,@,_,~,0-9,a-z,A-Z遇到非上述字元時,它會返回 UTF-8 碼。 比如 encodeURI(「囧」) 返回 「%E5%9B%A7″」 其中 「e5 9b a7」 就是 「囧」 的 UTF-8 碼。 上面也都說了,UTF-8 下大多漢字都會轉成 3 個位元組。encodeURIComponent:encodeURI 不轉碼的字元包括 !, 『,(,),*,-,。,_,~,0-9,a-z,A-Z同上它也是轉換成 utf-8 編碼格式。網路傳輸國際上通用的格式是 utf-8 , escape 雖不是 W3C 規範中的函數, 卻被所有瀏覽器都實現了。覺得本文對你有幫助?請分享給更多人關注「前端大全」,提升前端技能
推薦閱讀:

自帶「洪荒之力」的大海,會跳各種舞,弗拉明戈,華爾茲,芭蕾
天氣這麼熱,只有白色可以抑制住我的洪荒之力
有大神能系統整理描述洪荒神話么?
洪荒之力看澳洲多元文化! 悉尼最富和最窮的區竟然是... 你在哪個區?
用盡洪荒之力也要吃遍的香港小店

TAG:歷史 | 知識 | 編碼 | 時代 | 洪荒 |