刨根究底字符編碼之十一——UTF-8編碼方式與字節序標記

UTF-8碼方式與字節序標記

 

 

1、UTF-8碼方式正則表達式

1.算法

接下來將分別介紹Unicode字符集的三種碼方式:UTF-八、UTF-1六、UTF-32。這裏先介紹應用最爲普遍的UTF-8。瀏覽器

爲知足基於ASCII、面向字節的字符處理的須要,Unicode標準中定義了UTF-8碼方式。UTF-8應該是目前應用最普遍的一種Unicode碼方式(但不是最先面世的,UTF-16要早於UTF-8面世)。它是一種使用8位碼元(即單字節碼元)的變寬(即變長或不定長)碼元序列的碼方式。函數

因爲UTF-16對於ASCII字符也必須使用兩個字節(由於是16位碼元)進行碼,存儲和處理效率相對低下,而且因爲ASCII字符通過UTF-16碼後獲得的兩個字節,高字節始終是0x00,不少C語言的函數都將此字節視爲字符串末尾從而致使沒法正確解析文本。性能

所以,UTF-16一開始推出的時候就遭到不少西方國家的抵制,大大影響了Unicode的推行。因而後來又設計了UTF-8碼方式,才解決了這些問題。編碼

2.url

UTF-8的碼元由8位單字節組成;在UTF-8中,由於碼元較小的緣故,Unicode碼點值被映射到一個、兩個、三個或四個碼元;換言之,UTF-8使用一個至四個8位單字節碼元的序列來表示Unicode字符。spa

UTF-8碼方式對全部ASCII碼點值(0x00~0x7F)具備透明性。所謂透明性,具體指的是在U+0000到U+007F範圍內(十進制爲0~127)的Unicode碼點值,被直接轉換爲UTF-8單一字節碼元0x00~0x7F,與ASCII碼沒有區別。操作系統

而且,0x00~0x7F不會出如今UTF-8碼的非ASCII字符的首字節與非首字節的任意一個字節中(非ASCII字符的UTF-8碼爲由多個單字節碼元所組成的碼元序列),這樣就保證了與早已應用普遍且已成爲工業標準的ASCII碼的徹底兼容,避免了歧義,同時糾錯能力也強。設計

 

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

3.

UTF-8同其餘的多字節碼元碼方式相比具備如下優勢:

a)  UTF-8的碼空間足夠大,將來Unicode新標準收錄更多字符,UTF-8也能適應,所以不會再出現UTF-16那樣的尷尬。

(注:這裏所指的碼空間並非前文所提到的號空間Code Space,號空間屬於號字符集CCS裏的概念,而碼空間屬於字符碼方式CEF裏的概念,二者不能等同;這裏的碼空間可理解爲碼方式的將來可擴展性、高適應性,詳見後文《UTF-8到底是怎麼碼的——UTF-8的編碼算法介紹》以及《UTF-16到底是怎麼碼的——UTF-16的編碼算法介紹》)

b)  UTF-8是變長碼(準確地說是變長碼元序列,而碼元自己是固定長度爲8位單字節的,也就是說,UTF-8採用的單字節碼元),好比一個字節足以容納全部的ASCII字符,就用一個字節來存儲,沒必要在高位補0以浪費更多的字節來存儲,所以在英語做爲國際語言的現實狀況下,UTF-8因其ASCII字符的單字節碼這一特性可節省空間。

c)  UTF-8徹底直接兼容ASCII碼,而非不徹底間接兼容。

d)  UTF-8的碼元序列的第一個字節指明瞭後面所跟的字節的數目(即帶有前綴碼),這對字節流的前向解析很是有效(詳見後文《UTF-8到底是怎麼碼的——UTF-8的編碼算法介紹》)。

e)  也由於UTF-8碼帶有前綴碼,因此容錯性好,即便在傳輸過程當中發生局部的字節錯誤,好比即使丟失、增長、改變了某些字節,也不會致使全部後續字符所有錯亂這樣傳遞性、連鎖性的錯誤問題(不然,若存在錯誤傳遞性、連鎖性的話,一旦中間某些字節出錯,則必須丟棄從出錯點開始到結尾的全部碼字節,好比GB碼、UTF-32碼就是如此),所以很容易從新同步,具備很強的魯棒性(即健壯性)。

f)  因爲UTF-8碼沒有狀態,從UTF-8字節流的任意位置開始能夠有效地找到一個字符的起始位置,字符邊界很容易界定、檢測出來,因此具備很好的「自同步性」。

g)  UTF-8已經成爲互聯網所採用的字符碼方式的事實標準。

h)  UTF-8是字節順序無關的(由於是單字節碼元,而非像UTF-1六、UTF-32這樣的多字節碼元),它的字節順序在全部系統中都是同樣的,其碼元序列與字節序列相同,所以它實際上並不須要字節順序標記BOM(Byte-Orde Mark),雖然Windows系統常常「畫蛇添足」地加上BOM。(有關字節序標記BOM的介紹見下文)

字節序問題在進行信息交換時會帶來不小的麻煩。若是字節序未協商好,將致使亂碼;若協商結果爲雙方一個採用大端一個採用小端,則必然有一方要進行大小端轉換,性能損失不可避免(字節序的大小端問題其實不像看起來那麼簡單,有時會涉及硬件、操做系統、上層應用軟件多個層次,可能會致使屢次轉換,詳見前文中有關字節序Byte-Orde的介紹)。

i)  字節FE(二進制爲1111 1110)和FF(二進制爲1111 1111)在UTF-8碼中永遠不會出現(由於UTF-8碼方式中,每一個字節只能以0、1十、11十、11110或10開頭,詳見後文介紹)。所以能夠用稱之爲零寬度不中斷空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符名稱爲U+FEFF)做爲字節順序標記BOM來標明UTF-16或UTF-32文本的字節序。

(Windows系統中BOM有時也用在UTF-8碼的文本文件的開頭,雖然UTF-8碼不存在字節序問題,但Windows卻用BOM來代表該文本文件的碼格式爲UTF-8,看起來這有點「畫蛇添足」,其具體緣由詳見後文)

j)  UTF-8碼能夠經過屏蔽位和移位操做快速讀寫。

k)  字符串比較時strcmp()和wcscmp()的返回結果相同,所以使排序變得更加容易。

4.

UTF-8碼方式也並不是天衣無縫,大體上有以下缺點:

a)  沒法根據字符數直接判斷出UTF-8文本的字節數,由於UTF-8是一種變長碼方式(碼元雖然固定爲8位單字節,但碼元序列是變長的,多是單個碼元共8位,好比ASCII字符;也多是兩個碼元共16位、三個碼元共24位、四個碼元共32位等)。所以,不管是計算字符數,仍是執行索引操做,效率都不高。

b)  須要用2個字節碼那些在擴展ASCII(即EASCII)字符集中只需1個字節碼的擴展字符。

c)  以8位單字節碼元碼的UTF-8字符會被Email網關過濾,由於Internet上的信息傳輸最初設計爲7位ASCII碼字符(ASCII僅用到了1個字節的低7位)的傳輸。所以產生了UTF-7碼(相似於一樣爲Email傳輸而設計的Base64碼或quoted-printable碼,因爲Base64碼或quoted-printable碼各有其不足,所以又設計了UTF-7碼)。

d)  UTF-8在它的表示中使用值100xxxxx的概率超過50%,而現存的實現如ISO 202二、487三、6429和8859系統,會把它錯認爲是C1控制碼。所以產生了UTF-7.5碼。

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

 

2、字節序標記BOM

 

1.

在將邏輯形式的碼元序列(或可稱之爲邏輯碼)映射爲物理形式的字節序列(或可稱之爲物理碼)時,因系統平臺的差別,存在一個字節序(Byte-Order字節順序)的問題。Unicode/UCS規範中推薦的標記字節順序的方法是BOM字節序標記(Byte-Order Mark字節順序標記)。

字節序標記BOM是Unicode碼點值爲FEFF(十進制爲65279,二進制爲1111 1110 1111 1111)的字符的別名。

最初,字符U+FEFF若是出如今字節流的開頭,則用來標識該字節流的字節序——是高位在前仍是低位在前;若是它出如今字節流的中間,則表達爲該字符的原義——零寬度不中斷空格(ZERO WIDTH NO-BREAK SPACE零寬度無斷空白)。該字符名義上是個空格,其實是零寬度的,即至關因而不可見也不可打印字符(日常使用較多的是ASCII空格字符,是非零寬度的,須要佔用一個字符的寬度,爲可見不可打印字符)。

從Unicode 3.2開始,U+FEFF只能出如今字節流的開頭,且只能用於標識字節序,就如它的別名——字節序標記——所表示的意思同樣;除此之外的用法已被捨棄。取而代之的是,使用U+2060來表示零寬度不中斷空格。

2.

若是UTF-16碼的字節序列爲大端序,則該字節序標記在字節流的開頭呈現爲0xFE 0xFF;若字節序列爲小端序,則該字節序標記在字節流的開頭呈現爲0xFF 0xFE。若是UTF-32碼的字節序列爲大端序,則該字節序標記在字節流的開頭呈現爲0x00 0x00 0xFE 0xFF;若字節序列爲小端序,則該字節序標記在字節流的開頭呈現爲0xFF 0xFE 0x00 0x00。

UTF-8碼自己沒有字節序的問題,但仍然有可能會用到BOM——有時被用來標示某文本是UTF-8碼格式的文本;再強調一遍:在UFT-8碼格式的文本中,若是添加了BOM,則只用它來標示該文本是由UTF-8碼方式碼的,而不用來講明字節序,由於UTF-8碼不存在字節序問題。

3.

許多Windows程序(包含記事本)會添加BOM到UTF-8碼格式的文件中(至於爲何要添加BOM,可參看後續《微軟跟聯通有仇?》一文)。然而,在類Unix系統中,這種做法則不被建議採用。

由於它會影響到沒法識別它的程語言,如gcc會報告源碼文件開頭有沒法識別的字符;而在PHP中,若是沒有激活輸出緩衝(output buffering),它會使得頁面內容開始被送往瀏覽器(即header頭被提交),這使PHP腳本沒法指定header頭(HTTP Header)。

對於已在IANA註冊的字符編碼(這裏的字符編碼實際爲字符編碼模式CES)UTF-16BE、UTF-16LE、UTF-32BE和UTF-32LE等來講,不可以使用BOM。由於其名稱自己已決定了其字節順序。對於已註冊的字符編碼(這裏的字符編碼實際爲字符編碼方式CEF)UTF-16和UTF-32來講,則必須在文本開頭使用BOM。

4.

不一樣碼的字節序列中所使用的字節序標記BOM自己的字節序列呈現:

 

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

 

3、小結

1.

因爲UTF-8碼方式以一個字節(8位)做爲碼元,屬於單字節碼元,在計算機處理、存儲和傳輸時不存在字節序問題(字節序問題只跟多字節碼元有關),所以避免了平臺依賴性,跨平臺兼容性好。

它相對於其餘碼方式對英語更爲友好,一樣也對計算機語言(如C++、Java、C#、JavaScript、PHP、HTML等)更爲友好。它在處理ASCII等經常使用字符集時不多會比UTF-16低效。

2.

因此,UTF-8是較爲平衡、較爲理想的Unicode碼方式。雖然Windows平臺因爲歷史的緣由API缺少對UTF-8的原生支持(Windows原生支持的是UTF-16,由於UTF-16早於UTF-8面世),致使UTF-8推出後的早期使用不廣,但目前是應用最爲普遍的三大UTF碼方式之一。

所以,應該儘可能使用UTF-8(準確地說,應該儘可能使用UTF-8 without BOM,即不帶字節順序標記BOM的UTF-8)。

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

(未完待續)

 

預告:《刨根究底字符碼》系列的下一篇將重點剖析UTF-8到底是怎麼碼的(即UTF-8的編碼算法介紹);而《刨根究底正則表達式》系列的下一篇爲正則表達式簡介(上),包括正則表達式的概念、功能、簡史、流派介紹,敬請關注!】

相關文章
相關標籤/搜索