刨根究底字符編碼之十——Unicode字符集的編碼方式以及碼點、碼元


Unicode字符集的編碼方式以及碼點、碼元

 

一、字符編碼方式CEF的選擇

1.

由於Unicode字符集非常大,有些字符的編號(碼點值)需要兩個或兩個以上字節來表示,而要對這樣的編號進行編碼,也必須使用兩個或兩個以上字節。

比如,漢字“嚴”的Unicode碼(Unicode碼點值、Unicode編號)是十六進制數4E25,轉換成二進制數有15位(100 1110 0010 0101),對“嚴”這個字符的編號進行編碼的話,至少需要2個字節。表示其他更大編號的字符,可能需要3個字節或者4個字節,甚至更多。

2.

這帶來兩個問題:

一是,如何才能區別Unicode字符和ASCII字符的編碼?計算機怎么知道三個字節表示的是一個字符,而不是分別表示三個字符呢?

二是,我們知道,英文字母只用一個字節來編碼就夠了,而如果Unicode統一硬性規定,每個字符都用兩個、三個或四個字節來編碼,那么每個英文字母編碼的前面都必然有一個、兩個到三個字節全是0,這對於存儲和傳輸來說是極大的浪費。

這就涉及到了字符編碼方式CEF的選擇問題。Unicode字符的編碼方式一般有三種:UFF-8、UTF-16、UTF-32。在具體介紹這些編碼方式之前,需要再次深入了解兩個概念——碼點(Code Point)與碼元(Code Unit)。

 

二、碼點

1.

一個字符集一般可以用一張或多張由多個行和多個列所構成的二維表來表示。

二維表中行與列相交的點,稱之為碼點(Code Point代碼點),也稱之為碼位(Code position代碼位);每個碼點分配一個唯一的編號,稱之為碼點值或碼點編號,除開某些特殊區域(比如代理區、專用區)的非字符碼點和保留碼點,每個碼點唯一對應於一個字符。

因此,除開非字符碼點和保留碼點,碼點值(即碼點編號)通常來說就是其所對應的字符的編號,所以碼點值有時也可以直接稱之為字符編號,雖然不夠准確,但更為直接。

2.

字符集中所有碼點數量的總和,稱之為編號空間(Code Space,又被稱之為代碼空間、編碼空間、碼點空間、碼空間)。

碼點值最初用兩個字節的十六進制數字表示,比如字母A的Unicode碼點值為0041,常寫作U+0041,這種形式稱為Unicode碼點名稱,不嚴格地來講,也可稱之Unicode字符名稱(因為存在着非字符碼點和保留碼點,並非每個碼點都分配了字符,所以這種稱呼不夠准確,不過目前更為普遍)。

3.

后來隨着Unicode字符集的不斷增補擴大(比如現在的Unicode字符集至少需要21位才能全部表示),碼點值也擴展為用三個字節或以上的十六進制數字表示。

例如,ASCII字符集用0~127這連續的128個數字編號分別表示128個字符。GBK字符集使用區位碼的方式為每個字符編號,首先定義一個94×94的矩陣,行稱為“區”,列稱為“位”,然后將所有國標漢字放入矩陣當中,這樣每個漢字就可以用唯一的“區位”碼來標識了。例如“中”字被放到54區第48位,因此其區位碼(字符編號)就是5448。

而目前Unicode標准中,將字符按照一定的類別划分到0~16這17個平面(Plane層面)中,每個平面中擁有2^16 = 65536個碼點,因此,目前Unicode字符集所擁有的碼點總數,也就是Unicode的編號空間為17*65536=1114112。

 

注意,網絡上的很多文章中,代碼點、碼點、碼點值、碼值、代碼位、碼位、字符碼、Unicode碼、字符編號、字符編碼、編碼方案、編碼方式、編碼格式等等經常互相代替混用。

(笨笨阿林原創文章,轉載請注明出處)

 

三、碼元

1.

在計算機存儲和網絡傳輸時,碼點值(即字符編號)被映射到一個或多個碼元(Code Unit代碼單元、編碼單元)

碼元可理解為字符編碼方式CEF(Character Encoding Form)對碼點值進行編碼處理時作為一個整體來看待的最小基本單元(基本單位)

2.

為什么非要引入“碼元”這個概念?或者說,為什么非要強調“碼元”這個概念?

碼元某種程度上可認為對應於高級語言中的基本數據類型。而高級語言層面的基本數據類型,若要更深入一步地來講,實質上對應於機器硬件層面(匯編語言)的數據類型byte字節、word字、dword雙字等在硬件中的表達與處理機制。

之所以要強調“碼元”的概念,是因為字符編碼作為一串數字序列,最終還是得通過機器硬件層面的數據類型來表示。

而碼元的實質,就是機器硬件層面(匯編語言)的數據類型;不同的碼元,代表着不同位數的數據類型。

3.

數據類型有單字節與多字節之分,所以碼元也有單字節與多字節之分;多字節數據類型由於歷史的原因,存在着字節序的所謂大端序(Big-Endian)與小端序(Little-Endian)之分,因此多字節碼元也存在着大端序與小端序之分(具體詳見前文中有關字節序的解釋;注意,單字節數據類型則沒有字節序的問題,所以單字節碼元也就沒有字節序問題)。

這就是之所以要強調“碼元”這個概念的關鍵原因。

4.

碼點值(即字符編號)的具體實現方式——字符編碼方式CEF,就是由一個或多個碼元這樣的最小基本單元構成的。

最常用的碼元是8位(1字節)的單字節碼元,另外還有16位(2字節)和32位(4字節)兩種多字節碼元,分別相當於C++中的無符號整型BYTE、WORD、DWORD(在VC++6.0中,這三種數據類型的定義分別為:

typedef unsigned char BYTE;,1個字節;

typedef unsigned short WORD;,2個字節;

typedef unsigned long DWORD;,4個字節)。

(笨笨阿林原創文章,轉載請注明出處)

5.

於是,三種碼元對應就有了Unicode字符編號(碼點值)的三種UTF編碼方式(即Unicode碼轉換格式Unicode Transformation Format,或稱通用字符集轉換格式UCS Transformation Format):

UTF-8(8-bit Unicode/UCS Transformation Format),

UTF-16(16-bit Unicode/UCS Transformation Format),

UTF-32(32-bit Unicode/UCS Transformation Format);

或者反過來說,Unicode字符編號(碼點值)的三種UTF編碼方式(UTF-8、UTF-16、UTF-32)分別采用了不同的碼元(BYTE、WORD、DWORD)來編碼。

例如,“漢字”這兩個中文字符的Unicode碼點值(Unicode字符編號)是0x6C49和0x5B57,其三種UTF編碼在VC++6.0中可按如下定義進行“模擬”:

 6.

注意,這里之所以說是“模擬”,因為從本質上來講,在機器硬件層面上的所有數據類型,只存在着被視作一個整體來處理的比特序列(比特流)的位數不同之分,不存在着高級語言層面上數據類型的數值、字符串、布爾值等的語義不同之分。

因此,機器硬件層面上的數據類型與高級語言層面上的數據類型,嚴格來講,在本質含義上還是有着很大不同的。當然,高級語言層面上的數據類型最終還是會被轉化為機器硬件層面上的數據類型,畢竟計算機只“認識”由0和1所組成的比特流。具體詳見前文中有關字節序的解釋。

7.

這里用BYTE、WORD、DWORD分別表示無符號8位整數、無符號16位整數和無符號32位整數;因而UTF-8、UTF-16、UTF-32可認為分別以BYTE、WORD、DWORD作為碼元。

“漢字”這兩個中文字符的UTF-8編碼需要六個BYTE(共6個單字節碼元),大小是6個字節;UTF-16編碼需要兩個WORD(共2個雙字節碼元),大小是4個字節;UTF-32編碼需要兩個DWORD(共2個四字節碼元),大小是8個字節。

由於多字節數據類型的數據在計算機存取時存在一個字節序的問題,因此,UTF-16、UTF-32這兩種編碼方式所編碼出來的邏輯意義上的多字節碼元序列,在映射為物理意義上的字節序列時,字節序列的字節序因系統平台的不同而不同。

前面已經多次強調過了,這里再次特別強調一下:由單字節數據類型所組成的多字節數據是不存在字節序的問題的。因此,采用單字節碼元進行編碼的UTF-8編碼,雖然ASCII字符為單字節編碼,但非ASCII字符是多字節編碼的,但卻不存在字節序問題,這是跟同樣為多字節編碼、但采用多字節碼元的UTF-16、UTF-32不同之處。詳見下表所列:

 Unicode字符集三大編碼方式(UTF-8、UTF-16、UTF-32)比較一覽表 


(笨笨阿林原創文章,轉載請注明出處)

 

【預告:下一篇將重點講解UTF-8編碼方式與字節序標記(BOM),敬請關注!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM