關鍵術語解釋(下)
一、第1層 抽象字符表ACR (Abstract Character Repertoire抽象字符清單):明確字符的范圍(即確定支持哪些字符)
1.
抽象字符表ACR是一個編碼系統支持的所有抽象字符的集合,可以簡單理解為無序的字符集合,用於確定字符的范圍,即要支持哪些字符。
抽象字符表ACR的一個重要特點是字符的無序性,即其中的字符並沒有編排數字順序,當然也就沒有數字編號。
2.
“抽象”字符不具有某種特定的字形,不應與具有某種特定字形的“具體”字符混淆。
3.
字符表可以是封閉的(即字符范圍是固定的),即除非創建一個新的標准,否則不允許添加新的字符,比如ASCII字符表和ISO/IEC 8859系列都是這樣的例子;字符表也可以是開放的(即字符范圍是不固定的),即允許不斷添加新的字符,比如Unicode字符表和一定程度上Code Page代碼頁(代碼頁后文會有詳細解釋)是這方面的例子。
二、第2層 編號字符集CCS(Coded Character Set):用數字編號表示字符(即用數字給字符編號)
【注:一般將“Coded Character Set”翻譯為“編碼字符集”或“已編碼字符集”,但這里的“編碼”二字容易導致與后文的“編碼方式”及“編碼模式”中的“編碼”二字混淆,帶來理解上的困擾,因此覺得翻譯為“編號字符集”為宜。】
1.
前面講了,抽象字符表里的字符是沒有編排順序的,但無序的抽象字符表只能判斷某個字符是否屬於某個字符表,卻無法方便地引用、指稱該字符表中的某個特定字符。
為了更方便地引用、指稱字符表中的字符,就必須為抽象字符表中的每個字符進行編號。
所謂字符編號,就是將抽象字符表ACR中的中每個抽象字符(簡稱字符)表示為1個非負整數N或者映射到1個坐標(非負整數值對x, y),也就是將抽象字符的集合映射到一個非負整數或非負整數值對的集合,映射的結果就是編號字符集CCS。因此,字符的編號也就是字符的非負整數代碼。
例如,在一個給定的抽象字符表中,表示大寫拉丁字母“A”的字符被賦予非負整數65、字符“B”是66,如此繼續下去。
2.
由此產生了編號空間(Code Space,一般翻譯為代碼空間、碼空間、碼點空間)的概念:根據抽象字符表中抽象字符的數目,可以設定一個字符編號的上限值(該上限值往往設定為大於抽象字符表中的字符總數),從0到該上限值之間的非負整數范圍就稱之為編號空間。
編號空間的描述:
1)可以用一對非負整數來描述,例如:GB2312的漢字編號空間是94 x 94;
2)也可以用一個非負整數來描述,例如:ISO-8859-1的編號空間是256;也可以用字符的存儲單元尺寸來描述,例如:ISO-8859-1是一個8比特的編號空間(2^8=256);
3)還可以用其子集來描述,如行、列、面(Plane平面、層面)等等。
3.
編號空間中的一個位置(Position)稱為碼點(Code Point代碼點)或碼位(Code Position代碼位)。一個字符占用的碼點所在的坐標(非負整數值對)或所代表的非負整數,就是該字符的編號,又稱之為碼點值(即碼點編號)。
不過,嚴格來講,字符編號並不完全等同於碼點編號(即碼點值)。因為事實上,由於某些特殊的原因,編號字符集CSS里的碼點數量要大於抽象字符表ACR中的字符數量。
在編號字符集中,除了字符碼點之外,還存在着非字符碼點和保留碼點,所以字符編號不如碼點編號准確;但若對字符碼點的碼點編號稱之為字符編號,倒也更為直接。
在Unicode編碼方案中,字符碼點又稱之為Unicode標量值(Unicode Scalar Value,感覺不如字符碼點更為直觀),非字符碼點和保留碼點詳見后文介紹。
4.
注意,經常直接以“碼點”指代“碼點值”——當說到“碼點”的時候,有可能是在說編號空間(即碼點空間)中的一個位置,即碼點;也有可能實際是在說某個碼點的碼點值,即碼點編號。具體根據上下文而定。
這種類似的指代非常普遍,又比如“字符集”、“字符編號”和“字符編碼”這三個概念就經常相互指代。以“碼點”指代“碼點值”,根據上下文,倒也還不難理解;但“字符集”、“字符編號”和“字符編碼”三者也經常相互指代,雖然有其歷史原因,但目前的實際情況所導致的結果卻是使人迷惑、讓人抓狂!
5.
因此,所謂編號字符集,可簡單理解為就是把抽象字符進行逐個編號或者說逐個映射為碼點值(即碼點編號)后所得到的結果。編號字符集CSS常簡稱為字符集。
注意不要將編號字符集CSS和抽象字符表ACR相混淆。多個不同的編號字符集CCS可以表示同一個抽象字符表ACR。
6.
在Unicode標准中,一個單個的抽象字符,既有可能與多個碼點對應(為了與其它標准兼容,比如碼點編號為U+51C9與U+F979的這兩個碼點實際上是同一個字符“涼”,這是為了兼容韓國字符集標准KS X 1001:1998,具體可參看Unicode的官方文檔),也有可能使用一個由多個碼點所組成的碼點序列表示(為了表示由基本字符與組合字符組合在一起所組成的字符,比如à,由碼點編號為U+0061的基本字符字母“a”和碼點編號為U+0300的組合字符讀音符號“̀”組成);同時,也並非每一個碼點都對應於一個字符,也存在着非字符碼點或保留碼點。詳見后文解釋。
7.
特別注意:雖然“編號”與后文某種字符編碼方式CEF中的“編碼”(即碼元序列,解釋詳見后文)以及某種字符編碼模式CES中的“編碼”(即字節序列,解釋詳見后文)存在着對應的關系,但“編號”與“編碼”是截然不同的兩個概念。
對字符編號的過程——即確定字符碼點值的過程,跟計算機還沒有直接關系,可認為是一個純數學的問題,因為只是將字符與編號(即碼點值、碼點編號)對應起來;根本還沒涉及編碼算法的問題——即根據指定的字符編碼方式CEF對編號進行編碼以形成碼元序列,以及根據指定的字符編碼模式CES對碼元序列進一步編碼以形成字節序列。
但這兩個概念經常被混用,實實在在是讓人困惑的源頭,這是很令人無奈與遺憾的現實。
三、第3層 字符編碼方式CEF(Character Encoding Form字符編碼形式、字符編碼格式、字符編碼規則):將字符編號(即碼點值)編碼為碼元序列(即字符編碼)
1.
在講抽象字符表ACR時說過,不同於傳統的封閉的ASCII字符表,Unicode字符表是一個現代的開放的字符表,未來可能有更多的字符加入進來(比如很多Emoji表情符就被源源不斷地加入進來)。因此,Unicode編號字符集所需要的碼點數量,必然是會不斷增加的,相對來說是無限的。
但計算機所能表示的整數范圍卻是相對有限的。比如,一個無符號單字節整型數(unsigned char, uint8)能夠表示的編號只有0~0xFF,共256個;一個無符號雙字節短整型數(unsigned short, uint16)的能夠表示的編號只有0~0xFFFF,共65536個;而一個無符號四字節長整型數(unsigned long, uint32)能表示的碼位只有0~0xFFFFFFFF,共4294967296個。
2.
那么問題來了:
1)一方面,怎么通過相對有限的整型數來高可擴展地、高可適應地應對未來相對無限增長的字符數量呢?是用多個單字節整型數來間接表示,還是用一個足夠大的多字節整型數來直接表示?
2)另一方面,ASCII字符編碼作為最早出現、已被廣泛應用的編碼方案,完全不兼容顯然不明智,那么是直接兼容呢,還是間接兼容呢?
這兩方面的問題需要一個綜合解決方案,這就是字符編碼方式CEF(Character Encoding Form)。
3.
字符編碼方式CEF,是將編號字符集里字符的碼點值(即碼點編號、字符編號)轉換成或者說編碼成有限比特長度的編碼值(即字符編碼)。該編碼值實際上是碼元(Code Unit代碼單元、編碼單元)的序列(Code Unit Sequence)。
那什么是碼元呢?為什么要引入碼元這個概念呢?字符集里的字符編號又是如何轉換為計算機中的字符編碼(即碼元序列)的呢?別急,這里先記下這個概念,暫不深究,后文有詳細解釋。
4.
字符編碼方式CEF也被稱為“存儲格式”(Storage Format)。不過,將CEF稱之為存儲格式實際上並不合理,因為CEF還只是邏輯層面上的、與特定的計算機系統平台無關的編碼方式,尚未涉及到物理層面上的、與特定的計算機系統平台相關的存儲方式(第4層才涉及到)。
在ASCII這樣傳統的、簡單的字符編碼系統中,字符編號就是字符編碼,字符編號與字符編碼之間是一個直接映射關系。
而在Unicode這樣現代的、復雜的字符編碼系統中,字符編號不一定等於字符編碼,字符編號與字符編碼之間不一定是一個直接映射關系,比如UTF-8、UTF-16為間接映射,而UTF-32則為直接映射。
UTF-8、UTF-16與UTF-32等就是Unicode字符集(即編號字符集)常用的字符編碼方式CEF。(UTF-8、UTF-16與UTF-32后文各有詳細介紹)
5.
很多文章中,將Unicode與各UTF(Unicode/UCS Transformation Format,包括UTF-8、UTF-16與UTF-32等)之間的關系表述為:Unicode為標准規范、各UTF為編碼實現。
不嚴格地來講,也可以勉強這么認為。不過,這種表述畢竟不夠嚴謹,而且無助於理解Unicode標准(即Unicode編碼方案、Unicode編碼系統)、Unicode字符集與各UTF字符編碼方式之間更為深入細致的關系。
注:從現代字符編碼模型的角度來看,Unicode標准、Unicode編碼方案、Unicode編碼系統基本上為同義詞,是包括了抽象字符表ACR、編號字符集CCS、字符編碼方式CEF以及下面要講到的字符編碼模式CES甚至傳輸編碼語法TES在內的一整套標准方案系統,並不特指某一個層面。
四、第4層 字符編碼模式CES(Character Encoding Scheme):將碼元序列映射為字節序列
【注:一般將“Character Encoding Scheme”翻譯為“字符編碼方案”,但習慣上通常將某個字符編碼系統稱之為“字符編碼方案”,為避免引起理解上的困擾,應譯作“字符編碼模式”為宜。】
1.
字符編碼模式CES,也稱作“序列化格式”(Serialization Format),指的是將字符編號進行編碼之后的碼元序列映射為字節序列(即字節流),以便編碼后的字符在計算機中進行處理、存儲和傳輸。
如果說將編號字符集的碼點值(即字符編號)映射(編碼)為碼元序列的過程屬於跟特定的計算機系統平台無關的邏輯意義上的編碼,那么將碼元序列映射(編碼)為字節序列的過程就屬於跟特定的計算機系統平台相關的物理意義上的編碼。
2.
由於硬件平台與操作系統設計上的歷史原因,對於UTF-16、UTF-32等采用多字節碼元的編碼方式而言,必須使用一個原先稱之為零寬度不中斷空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符編號為U+FEFF)來指定字節序(Byte-Order字節順序、位元組順序,或稱為Endianness端序)是大端序還是小端序,計算機才能夠正確地進行處理、存儲和傳輸。(什么是字節序以及其大端序、小端序,解釋詳見后文)
不過,對於UTF-8這種采用單字節碼元的編碼方式來說,並不存在字節序問題,不需要指明字節序。因此,在各種計算機系統平台中,UTF-8編碼的碼元序列與字節序列都是相同的。(為什么UTF-8不存在字節序問題,解釋詳見后文)
3.
注意,由於字符編碼方式CEF與字符編碼模式CES中都有“編碼”二字,因此,通常所說的動詞編碼(Encode)有可能指的是通過字符編碼方式CEF將編號字符集CCS中的字符編號轉變為碼元序列;也有可能指的是通過字符編碼模式CES將字符編碼方式CEF中的碼元序列轉變為字節序列。
動詞解碼(Decode)則反過來,當然也同樣存在兩種可能。
而通常所說的名詞編碼(Coding、Encoding)也就相應地有可能指的是碼元序列,也有可能指的是字節序列。
因此,必須根據上下文語境來具體理解。
4.
對於程序員而言,通過字符編碼方式CEF編碼后所形成的碼元序列,更多的是一種邏輯意義上的中間編碼(即中介編碼,屬於從字符編號到字節序列的中間狀態,作為將字符編號轉換為字節序列的中介而存在),不是平時直接“打交道”的對象。
而通過字符編碼模式CES將碼元序列進一步編碼后所形成的字節序列,才是平時直接“打交道”最多的物理意義上的最終編碼(雖然事實上還有第5層的傳輸編碼語法TES所形成的編碼,但這種編碼畢竟僅用於某些特殊的傳輸環境,平時“打交道”的機會較少)。
五、第5層 傳輸編碼語法TES(Ttransfer Encoding Syntax):將字節序列作進一步的適應性編碼處理
由於歷史的原因,在某些特殊的傳輸環境中,需要對上一層次的字符編碼模式CES所提供的字節序列(字節流)作進一步的適應性編碼處理。一般包括兩種:
1)一種是把字節序列映射到一套更受限制的值域內,以滿足傳輸環境的限制,例如用於Email傳輸的Base64編碼或者quoted-printable編碼(可打印字符引用編碼),都是把8位的字節映射為7位長的數據(Email協議設計為僅能傳輸7位的ASCII字符);
2)另一種是壓縮字節序列的值,如LZW或者進程長度編碼等無損壓縮技術。
(這一部分內容本文不深入闡述,有興趣者可自行查找資料。)
六、總結一下現代字符編碼模型:
對於Unicode這樣的現代字符編碼系統來說,同一個字符因多個不同的字符編碼方式CEF(如UTF-8、UTF-16、UTF-32等)而具有多個不同的碼元序列(Code Unit Sequence),同一個碼元序列因兩個不同的字符編碼模式CES(大端序、小端序)而又可能具有兩個不同的字節序列(Byte Sequence)。
但這些不同的碼元序列也好,字節序列也好,只要表示的是同一個字符,所對應的碼點值(即碼點編號、字符編號)一般都是相同的(在Unicode標准中,為了與其它標准兼容,有少數字符可能與多個碼點對應)。
好了,關鍵術語先解釋到這里,其他術語將在后文中陸續解釋。