計算機字符編碼的前世今生

作者:vivo 互聯網服務器團隊 - Zhu Wenjin

一、前言

有人丟給你下面這張圖,如果你能清楚地說明它們之間的關係以及用途,那麼你對字符編碼的理解肯定過關了。

圖片

不知道看了上面這張圖,是否有混亂的感覺,本文試着給你梳理、講透這些孤立的幾個單詞之間聯繫......

二、關於字符編碼,你所需要知道的

2.1 ASCII(寡頭壟斷時期)

計算機內部,所有信息最終都是一個二進制值。每一個二進制位(bit)有 0 和 1 兩種狀態,8 個二進制位稱之爲 1 個字節。把鍵盤上(如下圖)所有按鍵的狀態。

圖片

用 8 位二進制共 256 種表示綽綽有餘。把所有的空格、標點符號、數字、大小寫字母分別用連續的字節狀態表示,一直編到了第 127 號,這樣計算機利用 8 位二進制位(1 個字節)就可以用來存儲英語的文字了,這就是大名鼎鼎的 ASCII(美國信息互換標準代碼)。當時世界上所有的計算機都用同樣的 ASCII 方案來保存英文文字。

2.2 非 ASCII 編碼(漢字編碼的發展)

從 128 到 255 這一頁的字符集被稱 “擴展字符集”。此之後,貪婪的人類再沒有新的狀態可以用了。

圖片

隨着計算機在中國流行時,已經沒有可以利用的字節狀態來表示漢字,況且有 6000 多個常用漢字需要保存。但這難不倒智慧的中國人民,我們就把那些 127 號之後的奇異符號們直接取消掉,並規定:一個小於 127 的字符的意義與原來相同,但兩個大於 127 的字符連在一起時,就表示一個漢字,前面的一個字節(他稱之爲高字節)從 0xA1 用到 0xF7,後面一個字節(低字節)從 0xA1 到 0xFE,這樣我們就可以組合出大約 7000 多個簡體漢字了。

在這些編碼裏,我們還把數學符號、羅馬希臘的字母都編進去了,連在 ASCII 裏本來就有的數字、標點、字母都統統重新編了兩個字節長的編碼,這就是常說的” 全角” 字符。而原來在 127 號以下的那些就叫” 半角” 字符了。中國人民看到這樣很不錯,於是就把這種漢字方案叫做 “GB2312”。GB2312 是對 ASCII 的中文擴展。

圖片

(圖片來源於網絡)

圖片

(圖片來源於網絡)

但是中國的漢字太多了,我們很快就就發現有許多人的人名沒有辦法在這裏打出來。我們不得不繼續把 GB2312 沒有用到的碼位找出來老實不客氣地用上。後來還是不夠用,於是乾脆不再要求低字節一定是 127 號之後的內碼,只要第一個字節是大於 127 就固定表示這是一個漢字的開始,不管後面跟的是不是擴展字符集裏的內容。

結果擴展之後的編碼方案被稱爲 GBK 標準,GBK 包括了 GB2312 的所有內容,同時又增加了近 20000 個新的漢字(包括繁體字)和符號。後來少數民族也要用電腦了,於是我們再擴展,又加了幾千個新的少數民族的字,GBK 擴成了 GB18030

2.3 非 ASCII 編碼

百花齊放,各自編碼標準帶來的問題

當時各個國家都像中國這樣搞出一套自己的編碼標準,結果互相之間誰也不懂誰的編碼,誰也不支持別人的編碼,就連大陸和臺灣這樣只相隔了 150 海里,使用着同一種語言,也分別採用了不同的 DBCS 編碼方案。

當時的中國人想讓電腦顯示漢字,就必須裝上一個 “漢字系統”,專門用來處理漢字的顯示、輸入的問題,像是那個臺灣的愚昧封建人士寫的算命程序就必須加裝另一套支持 BIG5

編碼的什麼 “倚天漢字系統” 纔可以用,裝錯了字符系統,顯示就會亂了套!這怎麼辦?

而且世界民族之林中還有那些一時用不上電腦的窮苦人民,他們的文字又怎麼辦?

圖片

(圖片來源於維基百科)

2.4 Unicode

世界這麼亂,得我來管管,大一統時期

ISO(國際標準化組織)的國際組織決定着手解決這個問題。採用的方法很簡單:廢了所有的地區性編碼方案,重新搞一個包括了地球上所有文化、所有字母和符號的編碼!

他們打算叫它 “Universal Multiple-Octet Coded Character Set”,簡稱 UCS, 俗稱 “Unicode”。Unicode 相當於一個抽象層,給每個字符一個唯一的碼點 (code point)

用 0x000000 - 0x10FFFF 這麼多的數字去對應全世界所有的語言、公式、符號。然後把這些數字分成 17 部分,把常用的放到 0x0000 - 0xFFFF,也就是 2 個字節,叫做基本平面 (BMP);從 0x010000 - 0x10FFFF 再劃分爲其他平面。

圖片

(圖片來源於維基百科)

栗子:「v 維」

圖片

如果 「v 維」 這個字符串放到內存中就是 0x767ef4。問題來了,計算機怎麼知道,幾個字節代表一個字符呢?是 0x76 呢?還是 0x7ef4 呢?還是 0x767ef4?

Unicode 只是對信源編碼,對字符集數字化,解決了字符到數字化的映射。接下來面臨如何解決存儲和傳輸的問題。

三、傳輸和存儲

 用通信理論的思路可以理解爲:

Unicode 是信源編碼,對字符集數字化;

UTF-32、UTF-16、UTF-8 是信道編碼,爲更好的存儲和傳輸。

3.1 UTF-32

UTF-32 編碼,簡單明瞭,碼點值是多少,內存中就存多少,UTF-32 缺點很明顯了,字母 A 原本只需要 1 個字節去存儲,而現在卻用了 4 個字節去存,大部分位置都是 0。

提問:我們爲什麼要多存那麼多零呢?

圖片

3.2 UTF-16

問題「亮」 碼點值是 20142,換成 16 進制就是 0x4eae,內存中是按字節進行編址的。所以我們是先存 4e 呢?還是 ae?

一個 Unicode 的碼點值會對應一個數字,對於 Basic 平面的字符,我們直接把這個數字存到內存中。

圖片

UTF - 16 編碼的時候,除本身的字節,爲了區分大端序和小端序,最開頭還多了兩個字節,ff 和 fe。feff 代表大端序,fffe 代表小端序。

圖片

(Notepad 中的 BOM)

小知識:feff 和 fffe 也叫做 BOM,它可以區分不同編碼。UTF-16 編碼最小單位是兩個字節,所以有字節序的問題,從而加了 BOM 來區分是大端序還是小端序。

3.3 UTF-8

UTF-8 顧名思義,是一套以 8 位爲一個編碼單位的可變長編碼。會將一個碼位編碼爲 1 到 4 個字節。一個碼點值會生成 1 個或多個字節,然後把這些字節按順序存就可以了。

小知識:UTF-8 無 BOM 或者 UTF-8 BOM。UTF - 8 的 BOM 是 EF BB BF  ,UTF-8 並不存在字節序的問題,因爲它的最小編碼單位就是字節。 

UTF-8 並不需要區分大端序還是小端序,所以可以不需要 BOM。如果加了 BOM,對於一些讀取操作,它可能會把讀取到的 BOM 認爲是字符,從而造成一些錯誤。所以我們保存 UTF - 8 編碼的文件時,最好選擇無 BOM。

圖片

栗子:「知」

圖片

根據上表中的編碼規則,「知」字的碼位 U+77E5 屬於第三行的範圍:

圖片

這就是將 U+77E5 按照 UTF-8 編碼爲字節序列 E79FA5 的過程,反之亦然。

圖片

3.4 ANSI

「ANSI」指的是對應當前系統 locale 的遺留(legacy)編碼。

圖片

Windows 裏說的「ANSI」其實是 Windows code pages,這個模式根據當前 locale 選定具體的編碼,比如簡中 locale 下是 GBK。把自己這些 code page 稱作「ANSI」是 Windows 的臭毛病。在 ASCII 範圍內它們應該是和 ASCII 一致的。

3.1 擴展思考

問:在 java 中 char 型變量中能不能存貯一箇中文漢字,爲什麼?

答:java 中使用的編碼符號集是 Unicode(不涉及特定的編碼方式,給每個符號分配一個二進制編碼,目前已容納容納 100 多萬個符號),而漢字已納入 Unicode 字符集, 而 char 類型佔兩個字節,用來表示 Unicode 編碼,所以是可以存儲漢字的

問:tomcat 的中默認 ISO-8859-1 編碼,如何解決 web 項目中的亂碼問題?

答:

**方式一:**修改 tomcat 下的 conf/server.xml 文件

 <Connector port="8080"  protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8 useBodyEncodingForURI="true"/>

**方式二: **

1)當使用字符流向瀏覽器發送頁面信息時,默認查詢的是 ISO-8859-1 碼錶

2)客戶端請求服務器出現的中文亂碼解決方式

String );//首先拿到參數的值
//得到的byte[] 再重新用utf-8去編碼,即可得到正常的值
);

說明:tomcat 的 j2ee 實現對錶單提交即 post 方式提示時處理參數採用缺省的 iso-8859-1 來處理,tomcat 對 get 方式提交的請求對 query-string 處理時採用了和 post 方法不一樣的處理方式。

四、總結

回到前言中的那個問題,整理了下面這張圖,不知現在的你是否對字符編碼有了更清楚的認識......

用通信理論的思路可以理解爲:

Unicode 是信源編碼,對字符集數字化;

UTF-32、UTF-16、UTF-8 是信道編碼,爲更好的存儲和傳輸。

圖片

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/vTh4LP8_Qew-S8s-3KqwJA