Unicode標準以及其常見的編碼方案

Unicode標準爲每一個字符提供一個唯一的數字,而不用區分平臺、語言等因素。

The Unicode Standard provides a unique number for every character, no matter what platform, device, application or language.

基本概念

在開始學習之前,我們需要先了解本文所涉及到的一些基本概念。

抽象字符(Abstract character):用於組織、控制或者表示文本數據的信息單元。

  • 抽象字符沒有具體的形式,不應與圖像字符(glyph)混淆。
  • 抽象字符不一定對應於人們所認知的“字”,不應與字素(grapheme)混淆。
  • 不能被Unicode標準直接編碼的抽象字符通常可以通過組合字符序列來表示。

抽象字符序列(Abstract character sequence):一個或多個抽象字符的有序序列。

Unicode編碼空間(Unicode codespace):十六進制0x0~0x10FFFF之間的整數。

碼位(Code point):Unicode編碼空間中的任意值。

編碼字符(Coded character):當抽象字符被映射或者分配到編碼空間中特定的碼位時,它就被稱爲編碼字符。

碼位

碼位是Unicode標準中很重要的一個概念。它的取值範圍是十六進制的0x0~0x10FFFF,換算成十進制是0~1114111,共計1114112個。

需要注意的是,一個單一的抽象字符可能對應一個以上的碼位。例如,Ω既可以表示大寫的希臘字母Omega,碼位是U+03A9,也可以表示物理學中的歐姆符號,碼位是U+2126

>>> '\u03a9'
'Ω'
>>> '\u2126'
'Ω'

單個抽象字符也可以由一系列碼位的序列來表示。例如,é的碼位是U+00E9,它也可以由小寫字母e(碼位爲U+0065)和́Combining Acute Accent)(碼位爲U+0301)組合而成。

>>> '\u00e9'
'é'
>>> '\u0065\u0301'
'é'

在Unicode標準中,碼位的表示方法通常是使用它們的十六進制,並加上U+前綴。

碼位的類型

碼位的分類方法多種多樣。我們通過下表來闡明Unicode標準使用的七種類型和一些術語。

基本類型 簡要描述 是否分配給抽象字符 碼位範圍
圖形(Graphic) 字母、標記、數字、標點符號、符號和空格
格式(Format) 不可見但是影響相鄰字符。包括行、段落分割符
控制(Control) Unicode標準以外的協議或標準定義的用法 U+0000~U+001F,U+007F,U+0080~U+009F,共計65個
私用(Private-use) Unicode標準以外的私有協議定義的用法
代理 (Surrogate) 永久預留給UTF-16編碼方案 不允許分配 U+D800~U+DFFF,共計2048個
非字符(Noncharacter) 永久預留給內部使用 U+FDD0~U+FDEF,所有以FFFE或者FFFF結尾的碼位,共計66個
保留(Reserved) 預留給將來使用

我們需要格外注意代理(Surrogate)類型,理解他有助於我們學習UTF-16。它總共包含2048個碼位,碼位空間爲U+D800~U+DFFF。它由引發出兩個新的概念:

高位代理(High-Surrogate):U+D800~U+DBFF範圍內的碼位,共計1024個。

低位代理(Low-Surrogate):U+DC00~U+DFFF範圍內的碼位,共計1024個。

關於它們更多的內容,稍後結合UTF-16再討論。

編碼方案

在介紹具體的編碼方案之前,我們先明確一些新的基本概念。

Unicode標量值(Unicode scalar value):除去高位代理和低位代理之外,所有的Unicode碼位,也就是U+0000~U+D7FF和U+E000~U+10FFFF範圍內的碼位。

編碼單元(Code unit):最小的比特位組合,表示用於交換或處理的編碼文本單元。Unicode標準中定義,UTF-8使用8比特的編碼單元,UTF-16使用16比特的編碼單元,UTF-32使用32比特的編碼單元。

編碼單元序列(Code unit sequence):一個或多個編碼單元的有序序列。

UTF-32

UTF-32將每個Unicode標量值映射成一個無符號的32比特的編碼單元,數值與Unicode標量值相同,這是一種定長的編碼方案。

注意,UTF-32無法編碼U+D800~U+DFFF之間的碼位,因爲它們不屬於Unicode標量值。

>>> '\ud800'.encode('utf32')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'utf-32' codec can't encode character '\ud800' in position 0: surrogates not allowed

UTF-16

UTF-16將Unicode標量值中U+0000~U+D7FFU+E000~U+FFFF範圍內的碼位映射成一個無符號的16比特的編碼單元,數值與Unicode標量值相同。將U+10000~U+10FFFF範圍內的碼位映射成一個代理對,所謂的代理對就是上文提到的高位代理和低位代理。UTF-16是一種定長和變長兼顧的編碼方案。

下表闡明瞭UTF-16的編碼方式。

Unicode標量值 UTF-16
xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
000u uuuu xxxx xxxx xxxx xxxx 1101 10ww wwxx xxxx 1101 11xx xxxx xxxx

其中, wwww = uuuuu - 1

重點分析使用代理對的情況,一個代理對包含一個高位代理編碼單元和一個低位代理編碼單元,都是16比特的。其中高位代理的範圍是U+D800~U+DBFF,轉換成二進制,它的格式應該是1101 10xx xxxx xxxx,低位代理的範圍是U+DC00~U+DFFF,轉換成二進制,它的格式應該是1101 11xx xxxx xxxx。

至此,我們還剩下20位可以填充。我們將碼位減去U+10000,再從右到左依次填充進去,就能得到UTF-16的編碼。

以字符𐌂Old Italic Letter Ke)爲例,它的碼位是U+10302,二進制表示是0000 0001 0000 0011 0000 0010,減去U+10000(二進制爲0000 0001 0000 0000 0000 0000),得到0000 0000 0000 0011 0000 0010。從右到左填充進模版,得到1101 1000 0000 0000 1101 1111 0000 0010,對應的十六進制是D800 DF02。

>>> '𐌂'.encode('utf-16be')
b'\xd8\x00\xdf\x02'

UTF-8

UTF-8將每個Unicode標量值映射成一到四個無符號的8比特的編碼單元,這是一種變長的編碼方案。

下表闡明瞭UTF-8的編碼方式。

Unicode標量值 第一個字節 第二個字節 第三個字節 第四個字節
00000000 0xxxxxxx 0xxxxxxx
00000yyy yyxxxxxx 110yyyyy 10xxxxxx
zzzzyyyy yyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
000uuuuu zzzzyyyy yyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx

仍然以字符𐌂Old Italic Letter Ke)爲例,它的碼位是U+10302,二進制表示是00000001 00000011 00000010,套用表中的模式,得到 11110000 10010000 10001100 10000010,對應的十六進制是F090 8C82。

>>> '𐌂'.encode('utf-8')
b'\xf0\x90\x8c\x82'

下表闡明瞭UTF-8的所有有效編碼範圍。

Unicode標量值範圍 第一個字節 第二個字節 第三個字節 第四個字節
U+0000~U+007F 00~7F
U+0080~U+07FF C2~DF 80~BF
U+0800~U+0FFF E0 A0~BF 80~BF
U+1000~U+CFFF E1~EC 80~BF 80~BF
U+D000~U+D7FF ED 80~9F 80~BF
U+E000~U+FFFF EE~EF 80~BF 80~BF
U+10000~U+3FFFF F0 90~BF 80~BF 80~BF
U+40000~U+FFFFF F1~F3 80~BF 80~BF 80~BF
U+100000~U+10FFFF F4 80~8F 80~BF 80~BF

參考資料

  1. UnicodeStandard-12.0:https://www.unicode.org/versions/Unicode12.1.0/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章