理解字元編碼

秉心說發表於2018-02-08

寫程式碼這麼久,突然發現並不瞭解字元編碼。我們每天寫下的程式碼,或者檔案,在計算機中究竟是怎樣的存在?我們都知道,計算機能夠認識的只有 01。所以,不論任何資料,最終在計算機中只是 01 的排列組合。那麼計算機時如何識別這些排列組合的呢?這就需要 字元編碼(Character encoding)了。簡單的說,就是按照一定的規則將資訊與其對應的 01 的排列組合對應起來,這樣計算機就可以根據字元編碼識別出硬碟中的排列組合所代表的真實資訊了。常見的 ASCII UTF-8 GBK 等等,都是典型的字元編碼。計算機到底是如何辨別這些字元編碼的,就要看一下具體的字元編碼原理。

ASCII

ASCII(American Standard Code for Information Interchange,美國資訊交換標準程式碼),是基於拉丁字母的一套電腦編碼系統。注意最後兩個字母是 II,而不是羅馬數字 2。ASCII 是由美國國家標準協會制定的,標準的單位元組字元編碼方案,用於基本文字的資料。起源於 50 年代後期,在 1967 年定案。它最初是美國國家標準,供不同計算機在相互通訊時用作共同遵守的西文字元編碼標準,它已被國際標準化組織定為國際標準,稱為 ISO 646 標準。適用於所有拉丁文字字母。

標準 ASCII 碼使用單字元,即 8 個二進位制位表示字元。第一位統一定為 0,實際使用後面 7 位來表示,所以 ASCII 碼一共規定了 128 個字元的編碼,包括所有的大小寫字母,數字 0 到 9,標點符號以及一些特殊控制字元。

標準 ASCII 碼錶如下:

理解字元編碼

ASCII 是美國標準,並不能滿足其他語言的需求。例如英鎊符號,中文漢字等等。西方一些國家使用 8 個二進位制位來表示字元,最多可以表示 256 個字元。顯然,對於漢字而言,一個字元不可能滿足需求。

ANSI

為了擴充 ASCII 編碼,以用於顯示本國的語言,不同的國家和地區制定了不同的標準,由此產生了 GB2312 , BIG5 , JIS 等各自的編碼標準。這些使用 2 個位元組來代表一個字元的各種漢字延伸編碼方式,稱為 ANSI 編碼,又稱為 MBCS(Muilti-Bytes Charecter Set,多位元組字符集)。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文作業系統下,ANSI 編碼代表 JIS 編碼,所以在中文 windows下要轉碼成 gb2312 , gbk 只需要把文字儲存為 ANSI 編碼 即可。不同的 ANSI 編碼並不相容,同一個二進位制值在不同的編碼體系中可能代表不同的字,這就導致了 Unicode 的誕生。在介紹 Unicode 之前,簡單看一下 ANSI 中的中文編碼。

GB2313

GB2312 也是 ANSI 編碼裡的一種,對 ANSI 編碼最初始的 ASCII 編碼進行擴充,為了滿足國內在計算機中使用漢字的需要,中國國家標準總局釋出了一系列的漢字字符集國家標準編碼,統稱為 GB碼 ,或國標碼。其中最有影響的是於 1980 年釋出的《資訊交換用漢字編碼字符集 基本集》,標準號為 GB 2312-1980 ,因其使用非常普遍,也常被通稱為國標碼。GB2312 編碼通行於我國內地;新加坡等地也採用此編碼。幾乎所有的中文系統和國際化的軟體都支援 GB 2312

GB2312 是一個簡體中文字符集,由 6763 個常用漢字和 682 個全形的非漢字字元組成。其中漢字根據使用的頻率分為兩級。一級漢字 3755 個,二級漢字 3008 個。

GBK

GB2312 的出現,基本滿足了漢字的計算機處理需要,但是對於人名,古漢語等方面的罕用字,GB2312 不能處理,這就導致了 GBK 的出現。

GBK 採用雙位元組表示,總體編碼範圍為 8140-FEFE ,首位元組在 81-FE 之間,尾位元組在 40-FE 之間,剔除 xx7F 一條線。總計 23940 個碼位,共收入 21886 個漢字和圖形符號,其中漢字(包括部首和構件)21003 個,圖形符號 883 個。

Unicode

ANSI 編碼的缺點很明顯,同一個編碼值,在不同的編碼體系中代表著不同的字元,很容易造成亂碼。如果有一種編碼,將世界上所有的符號都融入其中,每個符號都有對應的編碼值,這樣就不存在亂碼問題了。這就是 Unicode 編碼。

Unicode 編碼是一個很大的集合,現在的規模可以容納 100 多萬個符號,每個符號的編碼都不一樣。其實 Unicode 並不是真正意義上的字元編碼,它只是一個字符集,規定了符號的二進位制碼,卻沒有規定這個二進位制碼應該如何儲存。Unicode 有一些具體的實現編碼,其中用途最廣泛的莫屬 UTF-8

UTF-8

UTF-8 是使用最廣的 Unicode 的一種 Unicode 的實現方式 ,它是一種變長的編碼方式,使用 1 ~ 4 個字元表示一個符號,根據不同的符號而變化字元長度,提高了 Unicode 的編碼效率。先來看一下 UTF-8 對於不同位元組數的符號的表示方法, x 代表可使用的二進位制位:

位元組數 編碼規則 可表示字元數量
1 位元組 0xxxxxxx 2的7次方 = 128
2 位元組 11xxxxxx 10xxxxxx 2的11次方 = 2048
3 位元組 1110xxxx 10xxxxxx 10xxxxxx 2的15次方 = 65536
4 位元組 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 2的21次方 = 4194304

由上面的表很容易發現 UTF-8 的編碼規則:

  • 對於單位元組符號,第一位為 0,後面 7 為表示這個符號的 Unicode碼。所以對於單位元組符號,UTF-8 的表示方式與 ASCII 一致。

  • 對於 n 位元組的符號,第一個位元組的前 n 位都為 1,第 n+1 位為 0,後面的所有位元組前兩位均為 10,剩下的二進位制位為這個字元的 Unicode碼

使用這種變長編碼方式,對於單位元組的符號僅需使用一個位元組來表示,不會造成浪費。對於漢字來說,一般都是使用三個字元來表示。對於計算機來說,也很容易區分一個字元到底佔用幾個位元組:

  • 如果一個位元組的第一位為 0,這個位元組就是一個字元
  • 如果第一位為 1,連續有多少個 1,就表示當前字元佔用多少個位元組

除了 UTF-8,相應的還有 UTF-16。在 UTF-8 中,以 8 個二進位制位表示一個字元,而在 UTF-16 中,以 16 個二進位制位表示一個字元。16 個二進位制位可以直接表示 65536 個字元,所以在 UTF-16 中,漢字和英文字母具有同樣的地位,都是使用 16 個二進位制位,即 2 個位元組表示 1 個字元。對英文來說會造成浪費,但是對中文來說,可以節省儲存空間。

關於字元編碼,應該有了一個大概的認識,在日常使用中,我們要儘量做到編碼的統一,避免出現亂碼的情況。

參考文章:

文章同步更新於微信公眾號: 秉心說 , 專注 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!

理解字元編碼

相關文章