【转】刨根究底字符编码之十——Unicode字符集的字符编码方式
一、字符編碼方式CEF的選擇
1.
由于Unicode字符集非常大(并且作為開放字符集還在不斷擴展之中),有些字符的編號(即碼點值)需要兩個或兩個以上字節(jié)來表示,而要對這樣的編號進行編碼,也必須使用兩個或兩個以上字節(jié)。
比如,漢字“嚴”的Unicode編號以十六進制數(shù)表示為4E25,轉(zhuǎn)換成二進制數(shù)有15位(100 1110 0010 0101),對“嚴”這個字符的編號進行編碼的話,至少需要2個字節(jié)。表示其他更大編號的字符,可能需要3個字節(jié)或者4個字節(jié),甚至更多。
2.
這帶來兩個問題:
一是,如何才能區(qū)別Unicode字符和ASCII字符的編碼?計算機怎么知道三個字節(jié)表示的是一個字符,而不是分別表示三個字符呢?
二是,我們知道,英文字母只用一個字節(jié)來編碼就夠了,而如果Unicode統(tǒng)一硬性規(guī)定,每個字符都用兩個、三個或四個字節(jié)來編碼,那么每個英文字母編碼的前面都必然有一個、兩個到三個字節(jié)全是0,這對于存儲和傳輸來說是極大的浪費。
這就涉及到了字符編碼方式CEF的選擇問題。Unicode字符的編碼方式目前最常用的是這三種:UTF-8、UTF-16、UTF-32。在具體介紹這些編碼方式之前,需要再次深入了解兩個概念——碼點(Code Point)與碼元(Code Unit)。
?
二、碼點
1.
一個字符集一般可以用一張或多張由多個行和多個列所構(gòu)成的二維表來表示。
二維表中行與列相交的點,稱之為碼點(Code Point代碼點),也稱之為碼位(Code position代碼位);每個碼點分配一個唯一的編號,稱之為碼點值或碼點編號,除開某些特殊區(qū)域(比如代理區(qū)、專用區(qū))的非字符碼點和保留碼點,每個碼點唯一對應于一個字符。
因此,除開非字符碼點和保留碼點,碼點值(即碼點編號)通常來說就是其所對應的字符的編號,所以碼點值有時也可以直接稱之為字符編號,雖然不夠準確,但更為直接。
2.
字符集中所有碼點數(shù)量的總和,稱之為編號空間(Code Space,又被稱之為代碼空間、編碼空間、碼點空間、碼空間)。
碼點值最初用兩個字節(jié)的十六進制數(shù)字表示,比如字母A的Unicode碼點值為0041,常寫作U+0041,這種形式稱為Unicode碼點名稱,不嚴格地來講,也可稱之Unicode字符名稱(因為存在著非字符碼點和保留碼點,并非每個碼點都分配了字符,所以這種稱呼不夠準確,不過目前更為普遍)。
3.
后來隨著Unicode字符集的不斷增補擴大(比如現(xiàn)在的Unicode字符集至少需要21位才能全部表示),碼點值也擴展為用三個字節(jié)或以上的十六進制數(shù)字表示。
例如,ASCII字符集用0~127這連續(xù)的128個數(shù)字編號分別表示128個字符。GBK字符集使用區(qū)位碼的方式為每個字符編號,首先定義一個94×94的矩陣,行稱為“區(qū)”,列稱為“位”,然后將所有國標漢字放入矩陣當中,這樣每個漢字就可以用唯一的“區(qū)位”碼來標識了。例如“中”字被放到54區(qū)第48位,因此其區(qū)位碼(字符編號)就是5448。
而目前Unicode標準中,將字符按照一定的類別劃分到0~16這17個平面(Plane層面)中,每個平面中擁有2^16 = 65536個碼點,因此,目前Unicode字符集所擁有的碼點總數(shù),也就是Unicode的編號空間為17*65536=1114112。
注意,網(wǎng)絡上的很多文章中,代碼點、碼點、碼點值、碼值、代碼位、碼位、字符碼、Unicode碼、字符編號、字符編碼、編碼方案、編碼方式、編碼格式等等概念經(jīng)常互相代替混用,讓人困惑。
(笨笨阿林原創(chuàng)文章,轉(zhuǎn)載請注明出處)
?
三、碼元
1.
在計算機存儲和網(wǎng)絡傳輸時,碼點值(即字符編號)被映射到一個或多個碼元(Code Unit代碼單元、編碼單元)。
碼元可理解為字符編碼方式CEF(Character Encoding Form)對碼點值進行編碼處理時作為一個整體來看待的最小基本單元(或稱為最小基本單位)。
2.
為什么非要引入“碼元”這個概念?或者說,為什么非要強調(diào)“碼元”這個概念?
碼元某種程度上可認為對應于高級語言中的基本數(shù)據(jù)類型。而高級語言層面的基本數(shù)據(jù)類型,若要更深入一步地來講,實質(zhì)上對應于機器硬件層面(如匯編語言層面)的數(shù)據(jù)類型byte字節(jié)、word字、dword雙字等在硬件中的表達與處理機制。
之所以要強調(diào)“碼元”的概念,是因為字符編碼作為一串數(shù)字序列,最終還是得通過機器硬件層面的數(shù)據(jù)類型來表示。
而碼元的實質(zhì),就是機器硬件層面的數(shù)據(jù)類型;不同的碼元,代表著不同位數(shù)的數(shù)據(jù)類型。當字符編碼方案設計人員基于某種考慮(如時間復雜度、空間復雜度等)為某種字符編碼方式CEF選擇了某種碼元時,其實質(zhì)就是選擇了某個位數(shù)的數(shù)據(jù)類型。
數(shù)據(jù)類型,在機器硬件層面上來看,只有作為一個整體來處理的二進制數(shù)字位數(shù)上的不同(如byte字節(jié)、word字、dword雙字等匯編語言的數(shù)據(jù)類型,都是二進制數(shù)字值,只是位數(shù)不同而已),而并沒有高級語言層面上的數(shù)值、布爾、字符等語義的不同,畢竟本質(zhì)上來講計算機只“認識”由0和1組成的數(shù)字。
正因為如此,字符編碼方案設計人員在設計字符編碼方式CEF時,必然要選擇一種機器硬件層面上某個位數(shù)的數(shù)據(jù)類型(如byte字節(jié)、word字、dword雙字等)來表示,不是選擇這個位數(shù)的數(shù)據(jù)類型,就是選擇那個位數(shù)的數(shù)據(jù)類型。當然這種選擇會基于各種考慮,比如時間復雜度、空間復雜度等,不過一旦選擇了某種數(shù)據(jù)類型,其所選擇的數(shù)據(jù)類型位數(shù)上的不同,同時也就體現(xiàn)為了碼元位數(shù)上的不同。
所以,不同位數(shù)的碼元,實質(zhì)上是機器硬件層面上不同位數(shù)的數(shù)據(jù)類型。(而機器硬件層面上為什么要設計不同位數(shù)的多種數(shù)據(jù)類型出來,這是另一個話題了,有興趣可參看有關(guān)硬件方面的專著。)
3.
由于數(shù)據(jù)類型有單字節(jié)與多字節(jié)之分,所以碼元也有單字節(jié)與多字節(jié)之分;而多字節(jié)數(shù)據(jù)類型由于歷史的原因,存在著字節(jié)序的大端序(Big-Endian)與小端序(Little-Endian)之分,因此多字節(jié)碼元也存在著大端序與小端序之分(具體詳見前文中有關(guān)字節(jié)序的解釋;注意,單字節(jié)數(shù)據(jù)類型是沒有字節(jié)序的問題的,所以單字節(jié)碼元也就沒有字節(jié)序問題)。
這就是之所以要強調(diào)“碼元”這個概念的關(guān)鍵原因。
4.
對字符編號(即字符碼點值)進行編碼的具體實現(xiàn)方式——字符編碼方式CEF,就是由一個或多個碼元這樣的最小基本單元構(gòu)成的。
最常用的碼元是8位(即1字節(jié))的單字節(jié)碼元,另外還有16位(即2字節(jié))和32位(即4字節(jié))兩種多字節(jié)碼元,分別相當于匯編語言中的byte字節(jié)、word字、dword雙字,以及C++中的無符號整型BYTE、WORD、DWORD。
(注:?在VC++6.0中,這三種數(shù)據(jù)類型的定義分別為:
typedef unsigned char BYTE; // 1個字節(jié)
typedef unsigned short WORD; // 2個字節(jié)
typedef unsigned long DWORD; // 4個字節(jié))
(笨笨阿林原創(chuàng)文章,轉(zhuǎn)載請注明出處)
5.
于是,三種碼元對應就有了Unicode字符編號的三種UTF編碼方式(即Unicode碼轉(zhuǎn)換格式Unicode Transformation Format,或稱通用字符集轉(zhuǎn)換格式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)分別采用了不同的碼元(單字節(jié)、雙字節(jié)、四字節(jié))來編碼。
例如,“漢字”這兩個中文字符的Unicode字符編號是0x6C49和0x5B57,其三種UTF編碼在VC++6.0中可按如下定義進行“模擬”:
6.
注意,這里之所以說是“模擬”,其中的一個重要原因,如前文所述,從本質(zhì)上來講機器硬件層面上的所有數(shù)據(jù)類型,只存在著被視作一個整體來處理的比特序列(即二進制序列)的位數(shù)不同之分,不存在著高級語言層面上數(shù)據(jù)類型的數(shù)值、字符串、布爾值等的語義不同之分。
因此,機器硬件層面上的數(shù)據(jù)類型與高級語言層面上的數(shù)據(jù)類型,嚴格來講,在含義上還是有著很大不同的。當然,高級語言層面上的數(shù)據(jù)類型最終還是會被轉(zhuǎn)化為機器硬件層面上的數(shù)據(jù)類型,畢竟計算機只“認識”由0和1所組成的比特流。(可同時參見前文中有關(guān)字節(jié)序的解釋)
7.
這里用BYTE、WORD、DWORD分別表示無符號8位整數(shù)、無符號16位整數(shù)和無符號32位整數(shù);因而UTF-8、UTF-16、UTF-32可認為分別以BYTE、WORD、DWORD作為碼元。
“漢字”這兩個中文字符的UTF-8編碼需要六個BYTE(共6個單字節(jié)碼元),大小是6個字節(jié);UTF-16編碼需要兩個WORD(共2個雙字節(jié)碼元),大小是4個字節(jié);UTF-32編碼需要兩個DWORD(共2個四字節(jié)碼元),大小是8個字節(jié)。
由于多字節(jié)數(shù)據(jù)類型的數(shù)據(jù)在計算機存取時存在一個字節(jié)序的問題,因此,UTF-16、UTF-32這兩種編碼方式所編碼出來的邏輯意義上的多字節(jié)碼元序列,在映射為物理意義上的字節(jié)序列時,字節(jié)序列的字節(jié)序因系統(tǒng)平臺的不同而不同。
前面已經(jīng)多次強調(diào)過了,這里再次特別強調(diào)一下:由單字節(jié)數(shù)據(jù)類型所組成的多字節(jié)數(shù)據(jù)是不存在字節(jié)序的問題的。因此,采用單字節(jié)碼元進行編碼的UTF-8編碼,雖然ASCII字符為單字節(jié)編碼,而非ASCII字符卻是多字節(jié)編碼的,但卻并不存在字節(jié)序問題,這是跟同樣為多字節(jié)編碼、但卻采用多字節(jié)碼元的UTF-16、UTF-32編碼的不同之處。詳見下表所列。
Unicode字符集三大編碼方式(UTF-8、UTF-16、UTF-32)比較一覽表
(笨笨阿林原創(chuàng)文章,轉(zhuǎn)載請注明出處)
?
【預告:下一篇將重點講解UTF-8編碼方式與字節(jié)序標記(BOM),敬請關(guān)注!】
?
總結(jié)
以上是生活随笔為你收集整理的【转】刨根究底字符编码之十——Unicode字符集的字符编码方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 桥水基金是什么?桥水基金爆仓
- 下一篇: 【转】人工智能-1.1.1 什么是神经网