字符集和字元編碼學習總結
問題起源於,從網上下載的高清電影外掛字幕放到 QNap 中去,從 Qvideo 中訪問竟全是亂碼。查詢得知,QNap 中的 Video Station 只能解析識別以 UTF-8 編碼的字幕檔案。雖然採用 QNap 上更強大的 Plex 可以自然解決該問題,還是促使我要弄明白文字編碼到底是怎麼一回事。本以為文字編碼是一個簡單的問題,卻在網上查閱的過程中牽連出了一系列的問題。下面分別介紹。
1. 字符集與字元編碼
想要弄明白文字的編碼是怎麼回事,繞不過去字符集和字元編碼這兩個概念。
-
字符集:說白了就是某些特定字元的集合,如果把世界上不同國家文明的所有字元都放在一起組成一個集合,那麼我們常見的 ASCII、GB2312、GBK、GB18030、BIG5 字符集都只是包含了該集合的一部分而已。而 Unicode 字符集是可以包含所有國家文明中的所有字元的。
-
字元編碼:所有的檔案在計算機中最終是以二進位制序列來儲存的,不同的序列就可以表示不同的內容。字元編碼的目的就是對不同的字元編碼設計合理的唯一二進位制序列在計算機中進行儲存表示。
我們知道,位元組是計算機中對二進位制序列表達最常用的單位,一位元組的長度是8位,能夠表達256種不同的狀態,也就是256個不同的字元。
2. 非完備字符集&字元編碼
每一種技術的出現,伴隨其制定的標準在最初都無法囊括未來出現的所有情況。字元編碼就是如此。
2.1 ASCII 編碼
最開始,美國發明瞭計算機。與之相伴的,美國規定了能夠表達包括英文字母大小寫在內的128個字元(也包括了一些控制符,比如響鈴、退格等等),我們可以把這128個字元集合稱作 ASCII 字符集。伴隨著的是對應在計算機中的二進位制位元組編碼,也就可以稱作 ASCII 編碼。
2.2 擴充套件ASCII 編碼,ISO-8859-1編碼
為了表示更多的歐洲等國家使用的字元,對原始的 ASCII 編碼範圍進行了擴充,採用一個位元組 256 種不同狀態來表示 256 種不同的字元。
ISO-8859-1編碼是單位元組編碼,向下相容ASCII,其編碼範圍是0x00-0xFF,0x00-0x7F之間完全和ASCII一致,0x80-0x9F之間是控制字元,0xA0-0xFF之間是文字元號。
ISO-8859-1收錄的字元除ASCII收錄的字元外,還包括西歐語言、希臘語、泰語、阿拉伯語、希伯來語對應的文字元號。歐元符號出現的比較晚,沒有被收錄在ISO-8859-1當中。
ISO-8859-1 的較低部分(從 1 到 127 之間的程式碼)是最初的7位 ASCII。ISO-8859-1 的較高部分(從 160 到 255 之間的程式碼)全都有實體名稱。
因為ISO-8859-1編碼範圍使用了單位元組內的所有空間(即8位,0-255),在支援ISO-8859-1的系統中傳輸和儲存其他任何編碼的位元組流都不會被拋棄。換言之,把其他任何編碼的位元組流當作ISO-8859-1編碼看待都沒有問題。這是個很重要的特性,MySQL資料庫預設編碼是Latin1就是利用了這個特性。ASCII編碼是一個7位的容器,ISO-8859-1編碼是一個8位的容器。
Latin1是ISO-8859-1的別名,有些環境下寫作Latin-1。
2.3 GB2312、GBK、GB18030 編碼
為了解決漢字在計算機中的編碼問題,最開始推行的方案是採用兩個位元組對常用的 6763 個漢字和其它一些符號進行編碼。同時為了保證對 ASCII 編碼的相容性,每個位元組的最高一位位元總是為 1。這樣計算機遇到高位為1的位元組就會採用漢字編碼方案,遇到高位為0的位元組採用 ASCII 編碼方案。這種解決方法我們就可以稱為 GB2312 編碼方案。
雖然 GB2312 編碼能夠覆蓋99.75%使用頻率的漢字,畢竟還是有無法編碼的字存在。GBK 的出現彌補了少數漢字無法進行編碼解析的問題,它是 GB2312 編碼的擴充套件,向下相容 GB2312,同時包含了繁體字。
GB18030 進一步擴充套件了 GBK 所包含的字符集範圍,囊括了中國少數民族所用的字元等。同時也是向下相容 GBK、GB2312的。
2.4 BIG5、Shift_JIS、EUC-KR 編碼
與 GB2312 等編碼標準的出現相似,BIG5編碼 主要是臺灣地區為了解決對繁體字的處理。Shift_JIS 為日本電腦系統常用的編碼方案,EUC-KR 為韓國電腦系統常用編碼方案。
3. Unicode 字符集& UTF-8 編碼
3.1 Unicode 字符集
第2節中提到的各個編碼方案可以統歸為 ANSI 編碼。
如上 ANSI 編碼條例中所述,世界上存在著多種編碼方式,在 ANSI 編碼下,同一個編碼值,在不同的編碼體系裡代表著不同的字。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文作業系統下,ANSI 編碼代表 JIS 編碼,可能最終顯示的是中文,也可能顯示的是日文。在 ANSI 編碼體系下,要想開啟一個文字檔案,不但要知道它的編碼方式,還要安裝有對應編碼表,否則就可能無法讀取或出現亂碼。為什麼電子郵件和網頁都經常會出現亂碼,就是因為資訊的提供者可能是日文的 ANSI 編碼體系而資訊的讀取者可能是中文的編碼體系,他們對同一個二進位制編碼值進行顯示,採用了不同的編碼,導致亂碼。這個問題促使了 unicode 碼的誕生。
如果有一種編碼,將世界上所有的符號都納入其中,無論是英文、日文、還是中文等,大家都使用這個編碼表,就不會出現編碼不匹配現象。每個符號對應一個唯一的編碼,亂碼問題就不存在了。這就是 Unicode 編碼。
Unicode 現在的規模可以容納100多萬個符號。每個符號的編碼都不一樣,比如,U+0639表示阿拉伯字母Ain,U+0041表示英語的大寫字母A,“漢”這個字的Unicode編碼是U+6C49。
需要注意的是,Unicode只是一個字符集,它只規定了符號與二進位制程式碼之間的對應關係,卻沒有規定這個二進位制程式碼應該如何儲存。
比如,漢字"嚴"的unicode是十六進位制數4E25,轉換成二進位制數足足有15位(100111000100101),也就是說這個符號的表示至少需要2個位元組。表示其他更大的符號,可能需要3個位元組或者4個位元組,甚至更多。
這裡就有兩個嚴重的問題,第一個問題是,如何才能區別 Unicode 和 ASCII ?計算機怎麼知道三個位元組表示一個符號,而不是分別表示三個符號呢?第二個問題是,我們已經知道,英文字母只用一個位元組表示就夠了,如果 Unicode 統一規定,每個符號用三個或四個位元組表示,那麼每個英文字母前都必然有二到三個位元組是0,這對於儲存來說是極大的浪費,文字檔案的大小會因此大出二三倍,這是無法接受的。
3.2 UTF-8 編碼
UTF-8 是 Unicode 的實現方式之一。
UTF-8(UCSTransformation Format 8bit)就是在網際網路上使用最廣的一種 Unicode 的實現方式。其他實現方式還包括 UTF-16(字元用兩個位元組或四個位元組表示)和 UTF-32(字元用四個位元組表示)等,不過在網際網路上用的很少。
UTF-8 最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度。
UTF-8的編碼規則很簡單,只有二條:
-
對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的 unicode 碼。因此對於英語字母,UTF-8 編碼和 ASCII 碼是相同的。
-
對於n位元組的符號(n>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的 unicode 碼。
下表總結了編碼規則,字母x表示可用編碼的位。
Unicode 符號範圍(十六進位制) | UTF-8 編碼方式(二進位制) |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
跟據上表,解讀 UTF-8 編碼非常簡單。如果一個位元組的第一位是0,則這個位元組單獨就是一個字元;如果第一位是1,則連續有多少個1,就表示當前字元佔用多少個位元組。
下面以漢字"嚴"為例,演示如何實現 UTF-8 編碼。
已知"嚴"的 unicode 是 4E25(100111000100101),根據上表,可以發現 4E25 處在第三行的範圍內(0000 0800-0000 FFFF),因此"嚴"的 UTF-8 編碼需要三個位元組,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然後,從"嚴"的最後一個二進位制位開始,依次從後向前填入格式中的x,多出的位補0。這樣就得到了,“嚴"的UTF-8編碼是"11100100 10111000 10100101”,轉換成十六進位制就是E4B8A5。
4. 總結
經過上邊的介紹,我們可以大致認為,現在流行的一些編碼方案都是在相容 ASCII 的基礎上來實現的。為了滿足各國家地區的更多字元的編碼需求,出現了 ANSI 編碼標準,但是該編碼標準在具體各地區國家的實現上是彼此不相容的。為了滿足世界各國字元編碼的相容性需求,Unicode 定義了一個統一、完備的字符集。為了實現 Unicode 字符集在編碼上的需求,又誕生了 UTF-8、UTF-16等等編碼方案。
最後用一張圖來說明。