天天看點

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

1. 字元編碼術語

  • 字元集(character set): 可表達文本資訊的元素集合,如字母,數字,符号,圖像,控制字元等等。
  • 編碼空間(code space): 可用來編碼字元的數字值的範圍,不同的編碼,編碼空間不同。如US-ASCII的編碼空間是0x0 – 0x7F,Unicode的編碼空間是0x0 – 0x10FFFF。
  • Unicode: 可表示2個意思,一個是表示一個标準,全球統一字元集(UCS)标準。另一個意思是表示釋出這個标準的組織。本文沒有特别區分,因為根據上下文很容易區分。
  • 代碼點(code point): 來自代碼空間中任意值都是一個代碼點。注意,并不是每個代碼點都編碼了字元。如Unicode中代碼點區間U+D800 – U+DFFF就沒有編碼到字元。
  • 代碼值(code value): 使用特定的編碼方法編碼代碼點後得到的值就是代碼值。是指直接存儲到檔案中的值。
  • 代碼單元(code unit): 編碼值的最小機關。不同的編碼方式,代碼單元不一樣。比如UTF-8的代碼單元是8bit, 那麼編碼後的編碼值可能就是1/2/3/4位元組。UTF-16的代碼單元是16bit, 那麼編碼後的編碼值可能就是2/4位元組。
  • UCS: Universal Character Set的簡稱,是ISO/IEC提出的全球統一字元集。現在等同于Unicode。
  • UTF: Unicode(或者)Transformation Format,就是編碼Unicode标準的編碼方式。如UTF-8,UTF-16LE,UTF-16BE,UTF-32等。
  • BOM: Byte Order Mark, 在文本檔案的開頭插入标準代碼點,用來表示編碼的位元組序。

2. ASCII

全稱是美國資訊交換标準代碼(American Standard Code for Information Interchange),對應的國際标準是ISO/IEC 646。它是一個單位元組編碼字元集。編碼空間是0x00-0x7F,最高位為0。

詳細字元表如下圖,編碼表示了所有大寫字母,小寫字母,數字,标點符号,以及控制字元等共127個字元。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

3. ISO 8859标準系列

ISO 8859标準是基于标準ASCII進行擴充的單位元組編碼,具體點說就是把ASCII标準的最高位的0也拿來編碼。這個系列包括16個部分,其中 ISO 8859-12已經被放棄,ISO 8859-1得到了最廣泛的實作,是替代标準ASCII事實上的标準。然而它缺少歐洲符号,沒有完全覆寫芬蘭語和法語。ISO 8859-15在ISO 8859-1的基礎上增加了這部分修改。ISO 8859-15支援的語言包括:Albanian, Basque, Breton, Catalan, Danish, Dutch, English, Estonian, Faroese, Finnish, French, Frisian, Galician, German, Greenlandic, Icelandic, Irish Gaelic, Italian, Latin, Luxemburgish, Norwegian, Portuguese,Rhaeto-Romanic, Scottish Gaelic, Spanish, and Swedish.

ISO 8859系列标準的完整的字母表如下:
ISO 8859-1 West European languages (Latin-1)
ISO 8859-2 Central and East European languages (Latin-2)
ISO 8859-3 Southeast European and miscellaneous languages (Latin-3)
ISO 8859-4 Scandinavian/Baltic languages (Latin-4)
ISO 8859-5 Latin/Cyrillic
ISO 8859-6 Latin/Arabic
ISO 8859-7 Latin/Greek
ISO 8859-8 Latin/Hebrew
ISO 8859-9 Latin-1 modification for Turkish (Latin-5)
ISO 8859-10 Lappish/Nordic/Eskimo languages (Latin-6)
ISO 8859-11 Latin/Thai
ISO 8859-13 Baltic Rim languages (Latin-7)
ISO 8859-14 Celtic (Latin-8)
ISO 8859-15 West European languages (Latin-9)
ISO 8859-16 Romanian (Latin-10)

4. Unicode編碼

wiki上是這麼定義Unicode上是這麼定義的:統一編碼,表示和處理世界上大部分的文字系統的文本檔案的計算機工業标準。目前最新的标準是Unicode 9.0.0,包含128,000+的字元,符号,覆寫了135種現代語言,而且此标準還在不斷更新中,新的字元和符号會加到标準中。Unicode的成功,主要是由于它統一了字元集,導緻在計算機軟體國際化和本地化領域得到了廣泛的普及和使用。此标準在很多最新的技術中被實作,包括現代的作業系統,XML, Java, .NET等程式設計語言。

ISO先後也釋出了類似的Universal Coded Character Set (UCS) 标準ISO 10646,相同的數字編碼在兩個标準中表示的符号都是一樣的。差異在:

  • Unicode更新的更快,常會有新符号加入
  • Unicode有一些規則和規格是ISO沒有的,ISO隻是簡單的字元映射,相反,Unicode補充了整理和規範化形式的規則,給從右到左書寫的語言如阿拉伯語和希伯來語補充了雙向算法。對于平台之間的互操作性,特别是使用了雙向書寫語言的平台,實作ISO可能不夠,實作Unicode是必要的。

Unicode定義的代碼空間是0x0-0x10FFFF, 代碼空間中任意一個值就是一個代碼點(code point),Unicode的代碼點一般用”U+”加上它的十六進制數字表示,比如 U+0041表示大寫字母A的代碼點。整個Unicode代碼空間被劃分成17個等級(plane),标号從Plane0–Plane16, 這17個代碼等級被分成基本(Basic)和附加(Supplementary)兩大等級,代碼點在U+0000–U+FFFF範圍内的,屬于Plane 0, 等級名叫Basic Multilingual Plane(BMP)。代碼點在0x10000 – 0x10FFFF屬于Supplementary等級,Supplementary等級又可分成Plane1–Plane16 等16個子等級。每個子等級内的代碼點又可分成若幹個Block。

每個代碼點都有一個通用類别屬性。Unicode定義了這些屬性:Letter,Mark,Number, Punctuation, Symbol, Separator and Other,每個通用類别屬性還可以再細子屬性。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

Unicode标準中特殊的代碼點或者字元:

  • high-surrogate/low-surrogate代碼點

    high-surrogate, U+D800 – U+DBFF(1024個)。

    low-surrogate, U+DC00 – U+DFFF (1024個)。

    在Unicode中,這2048個代碼點本身是非法的,不對應任何字元。它們主要的作用是在UTF-16編碼中用來輔助編碼 U+10000 – U+10FFFF 這個區間的代碼點,具體如何編碼見後面的UTF-16編碼部分。

  • 66個非字元代碼點

    這66個代碼點不會用來編碼字元,這個集合是固定的,後續也不會增加。集合的代碼點包括 U+FDD0 – U+FDEF,以及以FFFE或FFFF結尾的代碼點,比如 U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, … U+10FFFE, U+10FFFF。

  • 預留代碼點

    這些代碼點可以用來編碼字元,但是目前标準沒有定義。

  • 可私用代碼點

    可私用代碼點是指可以用來編碼字元,但是目前标準沒有定義的代碼點。應用程式可拿來私用。需要交換資訊的應用程式,開發人員需要自己在發送方和接受方協商規範。可私用的代碼點有三個區間:

    • Private Use Area: U+E000–U+F8FF (6,400 characters)
    • Supplementary Private Use Area-A: U+F0000–U+FFFFD (65,534 characters)
    • Supplementary Private Use Area-B: U+100000–U+10FFFD (65,534 characters).
  • 圖像字元

    Unicode定義的有特定含義的圖像字元。

  • 格式字元

    Unicode中格式字元本身是不可見了,但是它的作用是可以影響它旁邊的字元的外觀和行為。

  • 65個作為控制字元的代碼點(U+0000–U+001F 和 U+007F–U+009F)

到目前為止,我們讨論的Unicode代碼點,與存儲方式是無關的,接下來重點說明Unicode代碼點的編碼方式。把代碼空間中代碼點映射到可存儲,可傳輸的一串值,這串值叫做代碼值(code value)。

4.1 UTF編碼

全稱叫Unicode Transformation Format。UTF-8和UTF-16是使用的最多的UTF編碼方式,根據Google在2016年11月的統計,現在WWW上87.9%的網頁是用UTF-8編碼的。Java語言中,char類型用的就是UTF-16。UTF的編碼包括UTF-1,UTF-7,UTF-8,UTF-16,UTF-32,詳細請點這裡,本文隻介紹最常用的編碼。

4.1.1 UTF-8

一種變長編碼,代碼單元(Code Unit)的長度是8-bit, 相容于ASCII。

UTF-8最早提出是在1993年,下圖是它的編碼方式。從這個表格可以看出,它可以編碼的代碼點 U+0000 – U+7FFFFFFF。編碼的長度從1位元組到6位元組不等。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

Unicode在1993年UTF-8版本的基礎上,增加了如下限制條件後,釋出了2003版的UTF-8,增加這些限制條件是為了比對UTF-16 編碼的限制條件。這些限制條件是:

* 去掉high and low surrogate定義的代碼點
* 代碼點隻編碼 0x0000 - U+10FFFF
           
字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

總結UTF-8 2003版的特性:

  • 相容于ASCII
  • 通過第一位元組的序列,可清楚的區分單位元組編碼和多位元組編碼
  • 通過第一位元組的序列,可清楚的指明編碼位元組序的長度
  • 字首特性,第一位元組可作為編碼序列的開始标記
  • 自同步

    這裡需要解釋一下,第一位元組可作為開始标記,後續的位元組以10xxxxxx開始,單位元組編碼的,以0xxxxxxx, 這些不一樣的值,在一定程度上可作為同步信号

  • 相對UTF-16更加節省空間

說了這麼多,我們宏觀地總結下UTF-8 2003版(當然UTF-16也适用)到底都編碼了哪些語言。前128個字元(US-ASCII)需要一個位元組。接下來的1920個字元需要兩個位元組來編碼。這涵蓋了幾乎所有的拉丁字母的剩餘部分,希臘語,斯拉夫語,科普特語,希伯來語,阿拉伯語,Syriac語,班巴拉語,以及組合變音符号。BMP裡的其它的字元需要三個位元組,其中包含中日韓經常使用的幾乎所有字元。Unicode中其它等級的字元需要四個位元組,包括不常見的中日韓字元,各種古文字,數學符号和表情符号(象形符号)等。

下表給出了幾個從Unicode代碼點轉化為UTF-8編碼的執行個體。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

許多Windows的應用程式包括記事本,會在文本檔案的開頭加上“0xEF, 0xBB, 0xBF”表示此檔案以UTF-8格式儲存。“0xEF, 0xBB, 0xBF”是UTF-8的Byte Order Mark,可簡寫成UTF-8 BOM。因為UTF-8本身是位元組序無關的,而且一些現有的能處理UTF-8的應用程式可能因為增加了UTF-8 BOM而出現問題,是以Unicode的标準既沒有強制要求,也沒有建議UTF-8使用BOM,但是如果使用的話也是允許的。

4.1.2 UTF-16

代碼單元是16-bit,可編碼的代碼點是 U+0000 – U+D800,U+DFFF – U+10FFFF。編碼值由1個或者2個代碼單元組成。

1980年後期,有IEEE和Unicode兩個組織在研究USC,早期人們以為2^16足以表示世界上的大部分的語言和文字,符号,是以UTF-16早期的設想是使用一個代碼單元就能表示世界上的大部分字元。但是,随着字元的增加,特别是中日韓字元的加入,16bit已經不能滿足需求。于是Unicode在1996年釋出了UTF-16 2.0,把能編碼的代碼空間從U+10000擴充到了U+10FFFF。國際标準ISO/IEC 10646,以及IETF RFC 2781也釋出了類似的标準。

UTF-16 編碼細節:

  • 對于代碼點屬于U+0000 – U+D7FF 和 U+E000 – U+FFFF

    這些代碼點都屬于BMP,編碼值和代碼點一一對應,使用1個 16bit 就可以表示了。

  • 對于代碼點屬于U+10000 – U+10FFFF

    假設某代碼點M 的取值範圍屬于U+10000 – U+10FFFF,

    設N = M - 0x10000, N 的屬于[0, 0xFFFFF]。使用20bit即可表示N。

    把N的二進制編碼的高10bit 表示記為N2,低10bit表示記為N1,其中N1,N2都屬于[0, 0x3FF]。

    那麼最終M的代碼值可表示為高代碼單元C2和低代碼單元C1兩個代碼單元,其中,

    C2 = 0xD800 + N2, C2 屬于[0xD800, 0xDBFF]

    C1 = 0xDC00 + N1, C1屬于[0xDC00, 0xDFFF]

    也就是說C2剛好屬于Unicode的high-surrogate編碼區,C1屬于Unicode的low-surrogate編碼區。

    high-surrogate/low-surrogate編碼區的代碼點在Unicode代碼空間是非法的,換句話說,正常字元的編碼是不會出現在high-surrogate/low-surrogate編碼區的,這樣就保證了UTF-16整個代碼值的唯一性。

下表給出了幾個從Unicode代碼點轉化為UTF-16編碼的執行個體。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

4.1.3 UTF-32

UTF-32 是一個定長的編碼方法,就是使用4位元組來直接表示Unicode代碼點。這樣的好處是代碼點和代碼值是直接對應的,它們間的互相轉化非常高效,缺點也明顯,就是浪費空間,因為文本中大部分字元都在BMP内,是以存儲空間基本是UTF-16的2倍,如果文本檔案都是ASCII字元的話,是UTF-8的約4倍。

4.1.4 UCS-2

從名字就可以看出,UCS-2,UCS-4标準最早是由ISO提出的。UCS-2直接使用2位元組來編碼BMP内的代碼點。由于UCS-2無法編碼代碼點超過BMP的字元,是以它的使用非常少。Unicode基于它進行了改進得到了UTF-16。

4.1.5 UCS-4

UCS-4直接使用4位元組的定長編碼方式。最早UCS-4 的編碼空間是0x0 – 0x7FFFFFFF。因為 ISO/IEC JTC 1/SC 2工作組2 規定,未來所有字元的配置設定都應該加上Unicode代碼點範圍的限制(即U+0000 – U+D800,U+DFFF – U+10FFFF)。是以UTF-32和UCS-4是基本一緻的。

4.1.6 UTF-16和UCS-2 BOM問題

由于UTF-16和UCS-2編碼産生的序列是一串16bit代碼單元,因為大部分通信和存儲協定是以位元組為機關的,一個UTF-16代碼單元有兩個位元組,這兩個位元組怎麼放就與位元組序有關了。為了更好地說明這個問題,我在Windows平台存儲了一個文本,裡面放在ABCD的字元串,然後分别存儲為ASCII, UTF-16LE, UTF-16BE三個格式,然後在Ubuntu上使用hexdump -b ABCD_ASCII.txt檢視位元組序列,注意是八進制的。可以發現:

  • ASCII檔案剛好是四個字母的ASCII,占用4個位元組。UTF-16的檔案的前面都多了2個位元組,UTF-16LE檔案前面多了0xFFFE,UTF-16BE檔案前面多了0xFEFF。
  • 小端LE和大端BE中每個代碼單元的位元組序剛好相反。
    字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

為了幫助識别代碼單元的位元組序,UTF-16 允許在檔案的開頭插入一個代碼點U+FEFF(U+FEFF 是一個zero-width non-breaking space/ZWNBS字元)作為BOM。當解碼器的位元組序和編碼器的位元組序一緻的情況下,它将識别到0xFEFF,當不一緻時,它識别到的是0xFFFE, 這樣解碼器處理後面的代碼單元時就需要交換位元組。Windows平台一般都使用了BOM, 類Unix系統一般不使用BOM。如果沒有BOM, 有一個方法可幫助識别位元組序,就是查找特别常用的空格字元(U+0020),看他的位元組就知道編碼器的位元組序了。

5. 中文編碼

5.1 GB2312

GB2312(1980)是中華人民共和國簡體中文編碼字元集的官方注冊網名。GB是GuojiaBiaozhun的縮寫。GB2312已經被GBK和GB18030标準取代了,據統計,2010年1月有3.5%的網站使用GB2312編碼,但是到了2016年4月,這個數字降到了0.8%。

GB2312的代碼點用94*94的表格表示,行号代表區号,編号從1到94,使用第一個位元組表示,列号代表區号中的位号,編号從1到94,使用第二個位元組表示,是以GB2312的代碼點也叫區位碼。

其中區号是這麼劃分的:

  • 01-09:由标點符号,特殊字元,以及Hiragana, Katakana, Greek, Cyrillic, Pinyin, Bopomofo組成
  • 10-15:預留
  • 16-55:漢字的第一個等級(plane),共3755個字元,根據拼音排列。
  • 56-87:漢字的第二個等級(plane),共3008個字元,根據筆畫排列。
  • 88-89:其它103個漢字,留給GB/T 12345标準使用
  • 90-94:預留

從區号劃分可知,GB2312共編碼了6763個漢字,以及一些其它字元。

在計算機程式裡,EUC-CN是編碼GB2321常用的字元編碼方式。它是一個2個位元組編碼方式,第一個位元組從0xA1-0xF7 (161-247),第二個位元組從0xA1-0xFE (161-254),因為2個位元組的取值範圍都不在[0, 127], 是以EUC-CN相容于ASCII。

根據上面的定義,可以得出從代碼點到編碼值的計算公式:

假設代碼點為P,代碼值的第一,第二個位元組分别是V1,V2,

那麼,V2 = 158 + P / 94,V1 = 158 + P % 94。

舉個栗子,漢字”外” 的代碼點是4566,那麼高位元組為0xCE=4566/94+158,

低位元組為0xD4=4566%94+158,漢字“外”的完整編碼是0xCED4=52948。

HZ是另外一個編碼GB2312的編碼方式,常用在email和新聞資訊編碼。

5.2 GBK

1993年,Unicode 1.1釋出,此标準包括20902個在中國大陸,台灣,日本,南韓使用的字元。接着,國家資訊産業部電子工業标準化研究所釋出了與Unicode 1.1相同的GB13000.1:1-93标準。

1993年,GBK标準釋出。GBK作為GB2312-80的一個擴充,除了GB2312-80定義的字元外,同時也包括GB13000.1:1-93定義的字元和GB2312未使用的代碼點。是以GBK相容于GB2312。GBK是“GuojiaBiaozhunKuozhan”的縮寫。據統計,截止2016年1月,網際網路上有0.3%的網頁使用GBK。Microsoft 在Windows 95和 Windows NT 3.51實作了GBK,盡管GBK不是官方标準,但是由于Windows 95的廣泛使用導緻GBK成了事實上的标準。

1995年,中國國家資訊技術标準化技術委員會制定的漢字内碼擴充規範1.0,也就是GBK1.0,它是基于GBK進行簡單的擴充,新增加的95個字元是GB13000.1:1-93未包含的,這些代碼在Unicode中屬于預留代碼點。

GBK字元要麼編碼成1個位元組,要麼是2個位元組。編碼是1個位元組時,就是US-ASCII定義的127個字元。如果某位元組的最高位為1,那麼它是2個位元組編碼。下表是GBK定義的代碼值。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

下圖是根據GBK代碼值劃分區域後的更加直覺的圖表

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

5.3 GB18030

GB18030-2005字元集的全稱是”中國國家标準GB18030-2005:資訊技術–漢字編碼字元集”,此标準除了定義了GB18030的編碼規範,還規定了在中國使用的軟體必須支援的字元和字型等。GB18030支援Unicode,包含簡體和繁體字元,相容于GB2312, CP936, 和 GBK 1.0。

GB18030是一個1/2/4個位元組(UTF)的變長編碼。1個位元組編碼的是US-ASCII(線性的),2個位元組編碼的是GBK定義的字元(非線性映射),4個位元組映射到Unicode中字元(線性的)。具體映射關系如下表。

字元編碼初探1. 字元編碼術語2. ASCII3. ISO 8859标準系列4. Unicode編碼5. 中文編碼6. 參考文獻

5.4 Big5

Big5是通行于台灣,香港,澳門的繁體字元編碼集。此标準是由5家公司共同研發的标準,由于缺少很多常用的字元,是以有很多公司對它進行了擴充。Big5是一個雙位元組編碼,第一個位元組從0x81到0xfe,第二個位元組從0x40到0x7e和從0xa1到0xfe。

BIG5的編碼可劃分為不同的區域:

0x8140 0xa0fe 預留給使用者自定義
0xa140 0xa3bf 圖像字元
0xa3c0 0xa3fe 預留,使用者不能自定義字元
0xa440 0xc67e 常用字
0xc6a1 0xc8fe 預留給使用者自定義
0xc940 0xf9d5 次常用字
0xf9d6 0xfefe 預留給使用者自定義

6. 參考文獻

  1. 幾乎所有編碼的代碼頁合集: https://en.wikipedia.org/wiki/Code_page_936
  2. 程式員趣味讀物:談談Unicode編碼: http://pcedu.pconline.com.cn/empolder/gj/other/0505/616631_all.html#content_page_1
  3. 字元集編碼與 C/C++ 源檔案字元編譯亂彈:[http://jimmee.iteye.com/blog/2165685
  4. 中文的幾個編碼 GB2312、GBK、GB18030、GB13000、BIG5: http://blog.csdn.net/blade2001/article/details/4390580
  5. 真正的百科全書,專業,全面: https://en.wikipedia.org/wiki/Wiki
  6. Unicode術語表: http://www.unicode.org/glossary/
  7. UTF-8, a transformation format of ISO 10646: http://www.ietf.org/rfc/rfc3629.txt

繼續閱讀