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

UTF-8編碼方式與字節(jié)序標記

一、UTF-8編碼方式

1.

接下來將分別介紹Unicode字符集的三種編碼方式:UTF-8蚜迅、UTF-16俊抵、UTF-32。這里先介紹應用最為廣泛的UTF-8徽诲。

為滿足基于ASCII吵血、面向字節(jié)的字符處理的需要,Unicode標準中定義了UTF-8編碼方式蹋辅。UTF-8應該是目前應用最廣泛的一種Unicode編碼方式(但不是最早面世的,UTF-16要早于UTF-8面世)侦另。它是一種使用8位碼元(即單字節(jié)碼元)的變寬(即變長或不定長)碼元序列的編碼方式。

由于UTF-16對于ASCII字符也必須使用兩個字節(jié)(因為是16位碼元)進行編碼褒傅,存儲和處理效率相對低下,并且由于ASCII字符經(jīng)過UTF-16編碼后得到的兩個字節(jié)殿托,高字節(jié)始終是0x00,很多C語言的函數(shù)都將此字節(jié)視為字符串末尾從而導致無法正確解析文本碌尔。

因此,UTF-16一開始推出的時候就遭到很多西方國家的抵制券敌,大大影響了Unicode的推行唾戚。于是后來又設計了UTF-8編碼方式,才解決了這些問題待诅。

2.

UTF-8的碼元由8位單字節(jié)組成叹坦;在UTF-8中,因為碼元較小的緣故卑雁,Unicode碼點值被映射到一個募书、兩個、三個或四個碼元测蹲;換言之莹捡,UTF-8使用一個至四個8位單字節(jié)碼元的序列來表示Unicode字符。

UTF-8編碼方式對所有ASCII碼點值(0x00~0x7F)具有透明性扣甲。所謂透明性篮赢,具體指的是在U+0000到U+007F范圍內(nèi)(十進制為0~127)的Unicode碼點值,被直接轉(zhuǎn)換為UTF-8單一字節(jié)碼元0x00~0x7F琉挖,與ASCII碼沒有區(qū)別启泣。

并且,0x00~0x7F不會出現(xiàn)在UTF-8編碼的非ASCII字符的首字節(jié)與非首字節(jié)的任意一個字節(jié)中(非ASCII字符的UTF-8編碼為由多個單字節(jié)碼元所組成的碼元序列)示辈,這樣就保證了與早已應用廣泛且已成為工業(yè)標準的ASCII碼的完全兼容寥茫,避免了歧義,同時糾錯能力也強矾麻。

(笨笨阿林原創(chuàng)文章纱耻,轉(zhuǎn)載請注明出處)

3.

UTF-8同其他的多字節(jié)碼元編碼方式相比具有以下優(yōu)點:

a) ?UTF-8的編碼空間足夠大芭梯,未來Unicode新標準收錄更多字符,UTF-8也能適應膝迎,因此不會再出現(xiàn)UTF-16那樣的尷尬粥帚。

(注:這里所指的編碼空間并不是前文所提到的編號空間Code Space,編號空間屬于編號字符集CCS里的概念限次,而編碼空間屬于字符編碼方式CEF里的概念,兩者不能等同柴灯;這里的編碼空間可理解為編碼方式的未來可擴展性卖漫、高適應性,詳見后文《UTF-8究竟是怎么編碼的——UTF-8的編碼算法介紹》以及《UTF-16究竟是怎么編碼的——UTF-16的編碼算法介紹》)

b) ?UTF-8是變長編碼(準確地說是變長碼元序列赠群,而碼元本身是固定長度為8位單字節(jié)的,也就是說突委,UTF-8采用的單字節(jié)碼元)冬三,比如一個字節(jié)足以容納所有的ASCII碼字符,就用一個字節(jié)來存儲敌蚜,不必在高位補0以浪費更多的字節(jié)來存儲弛车,因此在英語作為國際語言的現(xiàn)實情況下蒲每,UTF-8因其ASCII字符的單字節(jié)編碼這一特性可節(jié)省空間贫奠。

c) ?UTF-8完全直接兼容ASCII碼,而非不完全間接兼容泣特。

d) ?UTF-8的碼元序列的第一個字節(jié)指明了后面所跟的字節(jié)的數(shù)目(即帶有前綴碼)状您,這對字節(jié)流的前向解析非常有效(詳見后面的附文《UTF-8是怎么編碼的——UTF-8的編碼算法介紹》)眯分。

e) ?也因為UTF-8編碼帶有前綴碼弊决,所以容錯性好飘诗,即使在傳輸過程中發(fā)生局部的字節(jié)錯誤,比如即便丟失净响、增加夫椭、改變了某些字節(jié),也不會導致所有后續(xù)字符全部錯亂這樣傳遞性羽莺、連鎖性的錯誤問題(否則,若存在錯誤傳遞性、連鎖性的話,一旦中間某些字節(jié)出錯鱼蝉,則必須丟棄從出錯點開始到結尾的所有編碼字節(jié),比如GB碼洁奈、UTF-16碼就是如此)镀赌,因此很容易重新同步姆打,具有很強的魯棒性(即健壯性)。

f) ?由于UTF-8編碼沒有狀態(tài)痊剖,從UTF-8字節(jié)流的任意位置開始可以有效地找到一個字符的起始位置叮贩,字符邊界很容易界定、檢測出來捺萌,所以具有很好的“自同步性”。

g) ?UTF-8已經(jīng)成為互聯(lián)網(wǎng)所采用的字符編碼方式的事實標準驮配。

h) ?UTF-8是字節(jié)順序無關的(因為是單字節(jié)碼元琐旁,而非像UTF-16牺陶、UTF-32這樣的多字節(jié)碼元)怀估,它的字節(jié)順序在所有系統(tǒng)中都是一樣的,其碼元序列與字節(jié)序列(字節(jié)流)相同,因此它實際上并不需要字節(jié)順序標記BOM(Byte-Orde Mark),雖然Windows系統(tǒng)經(jīng)常“多此一舉”地加上BOM榛鼎。(有關字節(jié)序標記BOM的介紹見下文)

字節(jié)序問題在進行信息交換時會帶來不小的麻煩黄鳍。如果字節(jié)序未協(xié)商好,將導致亂碼拧晕;若協(xié)商結果為雙方一個采用大端一個采用小端蔫敲,則必然有一方要進行大小端轉(zhuǎn)換貌虾,性能損失不可避免(字節(jié)序的大小端問題其實不像看起來那么簡單袄膏,有時會涉及硬件、操作系統(tǒng)揖盘、上層應用軟件多個層次箕慧,可能會導致多次轉(zhuǎn)換斩熊,詳見前文中有關字節(jié)序Byte-Orde的介紹)。

i) ?字節(jié)FE(二進制為1111 1110)和FF(二進制為1111 1111)在UTF-8編碼中永遠不會出現(xiàn)(因為UTF-8編碼方式中淳衙,每個字節(jié)只能以0靴跛、110、1110腹鹉、11110或10開頭,詳見后文介紹)诫硕。因此可以用稱之為零寬度不中斷空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符名稱為U+FEFF)作為字節(jié)順序標記BOM來標明UTF-16或UTF-32文本的字節(jié)序藕届。

(Windows系統(tǒng)中BOM有時也用在UTF-8編碼的文本文件的開頭踏兜,雖然UTF-8編碼不存在字節(jié)序問題,但Windows卻用BOM來表明該文本文件的編碼格式為UTF-8上忍,看起來這有點“多此一舉”繁成,其具體原因詳見后文)

j) ?UTF-8編碼可以通過屏蔽位和移位操作快速讀寫祠墅。

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

4.

UTF-8編碼方式也并非完美無缺,大致上有如下缺點:

a) ?無法根據(jù)字符數(shù)直接判斷出UTF-8文本的字節(jié)數(shù),因為UTF-8是一種變長編碼方式(碼元雖然固定為8位單字節(jié)胆绊,但碼元序列是變長的跟继,可能是單個碼元共8位剩盒,比如ASCII字符纪挎;也可能是兩個碼元共16位烤蜕、三個碼元共24位泡徙、四個碼元共32位等)。因此,無論是計算字符數(shù)蜘矢,還是執(zhí)行索引操作寓搬,效率都不高镣典。

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

c) ?以8位單字節(jié)碼元編碼的UTF-8字符會被Email網(wǎng)關過濾,因為Internet上的信息傳輸最初設計為7位ASCII碼字符(ASCII僅用到了1個字節(jié)的低7位)的傳輸。因此產(chǎn)生了UTF-7編碼(類似于同樣為Email傳輸而設計的Base64編碼或quoted-printable編碼胆建,由于Base64編碼或quoted-printable編碼各有其不足,因此又設計了UTF-7編碼)魄懂。

d) ?UTF-8在它的表示中使用值100xxxxx的幾率超過50%,而現(xiàn)存的實現(xiàn)如ISO 2022、4873勾效、6429和8859系統(tǒng)其监,會把它錯認為是C1控制碼锌历。因此產(chǎn)生了UTF-7.5編碼。

(笨笨阿林原創(chuàng)文章,轉(zhuǎn)載請注明出處)

二、字節(jié)序標記BOM

1.

在將邏輯形式的碼元序列(或可稱之為邏輯編碼)映射為物理形式的字節(jié)序列(或可稱之為物理編碼)時实幕,因系統(tǒng)平臺的差異,存在一個字節(jié)序(Byte-Order字節(jié)順序)的問題辉川。Unicode/UCS規(guī)范中推薦的標記字節(jié)順序的方法是BOM字節(jié)序標記(Byte-Order Mark字節(jié)順序標記)集索。

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

最初咽安,字符U+FEFF如果出現(xiàn)在字節(jié)流的開頭沸伏,則用來標識該字節(jié)流的字節(jié)序——是高位在前還是低位在前姆另;如果它出現(xiàn)在字節(jié)流的中間,則表達為該字符的原義——零寬度不中斷空格(ZERO WIDTH NO-BREAK SPACE零寬度無斷空白)。該字符名義上是個空格,實際上是零寬度的,即相當于是不可見也不可打印字符(平常使用較多的是ASCII空格字符茶行,是非零寬度的,需要占用一個字符的寬度姿锭,為可見不可打印字符)伯铣。

從Unicode 3.2開始,U+FEFF只能出現(xiàn)在字節(jié)流的開頭焚鲜,且只能用于標識字節(jié)序忿磅,就如它的別名——字節(jié)序標記——所表示的意思一樣犀斋;除此以外的用法已被舍棄叽粹。取而代之的是虫几,使用U+2060來表示零寬度不中斷空格辆脸。

2.

如果UTF-16編碼的字節(jié)序列為大端序,則該字節(jié)序標記在字節(jié)流的開頭呈現(xiàn)為0xFE 0xFF状囱;若字節(jié)序列為小端序,則該字節(jié)序標記在字節(jié)流的開頭呈現(xiàn)為0xFF 0xFE袭艺。如果UTF-32編碼的字節(jié)序列為大端序叨粘,則該字節(jié)序標記在字節(jié)流的開頭呈現(xiàn)為0x00 0x00 0xFE 0xFF;若字節(jié)序列為小端序答倡,則該字節(jié)序標記在字節(jié)流的開頭呈現(xiàn)為0xFF 0xFE 0x00 0x00瘪撇。

UTF-8編碼本身沒有字節(jié)序的問題设江,但仍然有可能會用到BOM——有時被用來標示某文本是UTF-8編碼格式的文本叉存;再強調(diào)一遍:在UFT-8編碼格式的文本中,如果添加了BOM歼捏,則只用它來標示該文本是由UTF-8編碼方式編碼的瞳秽,而不用來說明字節(jié)序率翅,因為UTF-8編碼不存在字節(jié)序問題冕臭。

3.

許多Windows程序(包含記事本)會添加BOM到UTF-8編碼格式的文件中(至于為什么要添加BOM辜贵,可參看后續(xù)《微軟跟聯(lián)通有仇?》一文)托慨。然而,在類Unix系統(tǒng)中蔼紧,這種作法則不被建議采用狠轻。

因為它會影響到無法識別它的編程語言哈误,如gcc會報告源碼文件開頭有無法識別的字符蜜自;而在PHP中卢佣,如果沒有激活輸出緩沖(outputbuffering)虚茶,它會使得頁面內(nèi)容開始被送往瀏覽器(即header頭被提交)嘹叫,這使PHP腳本無法指定header頭(HTTP Header)刨肃。

對于已在IANA注冊的字符編碼(實際為字符編碼模式CES)UTF-16BE、UTF-16LE、UTF-32BE和UTF-32LE等來說杠步,不可使用BOM或粮。因為其名稱本身已決定了其字節(jié)順序捞高。對于已注冊的字符編碼UTF-16和UTF-32來說硝岗,則必須在文本開頭使用BOM辈讶。

4.

不同編碼的字節(jié)序列中所使用的字節(jié)序標記BOM本身的字節(jié)序列呈現(xiàn):

(笨笨阿林原創(chuàng)文章,轉(zhuǎn)載請注明出處)

三媳溺、小結

1.

由于UTF-8編碼方式以一個字節(jié)(8位)作為碼元悬蔽,屬于單字節(jié)碼元蝎困,在計算機處理倍啥、存儲和傳輸時不存在字節(jié)序問題(字節(jié)序問題只跟多字節(jié)碼元有關),因此避免了平臺依賴性始藕,跨平臺兼容性好伍派。

它相對于其他編碼方式對英語更為友好诉植,同樣也對計算機語言(如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 withoutBOM,即不帶字節(jié)順序標記BOM的UTF-8)狮荔。

(笨笨阿林原創(chuàng)文章晚树,轉(zhuǎn)載請注明出處)

(未完待續(xù))

預告:《刨根究底字符編碼》系列的下一篇將重點剖析UTF-8究竟是怎么編碼的(即UTF-8的編碼算法介紹)总滩,敬請關注!】


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

下一篇:刨根究底字符編碼之十二——UTF-8究竟是怎么編碼的

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乒验,一起剝皮案震驚了整個濱河市狂塘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廊营,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件假哎,死亡現(xiàn)場離奇詭異,居然都是意外死亡扇救,警方通過查閱死者的電腦和手機靠娱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門迅诬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闲礼,“玉大人,你說我怎么就攤上這事〉谥螅” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵鲫剿,是天一觀的道長。 經(jīng)常有香客問我,道長明场,這世上最難降的妖魔是什么嫌套? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任痹筛,我火速辦了婚禮滋早,結果婚禮上,老公的妹妹穿的比我還像新娘饼问。我一直安慰自己驮吱,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布除破。 她就那樣靜靜地躺著光坝,像睡著了一般鸳惯。 火紅的嫁衣襯著肌膚如雪辅鲸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音,去河邊找鬼捐顷。 笑死唉地,一個胖子當著我的面吹牛讼昆,可吹牛的內(nèi)容都是我干的既峡。 我是一名探鬼主播迄沫,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼逝她,長吁一口氣:“原來是場噩夢啊……” “哼酗宋!你這毒婦竟也來了隆圆?” 一聲冷哼從身側響起蹬屹,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤更鲁,失蹤者是張志新(化名)和其女友劉穎纠亚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摊鸡,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡囤热,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了经宏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徊都。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡房轿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寿冕,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布驼唱,位于F島的核電站藻茂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辨赐,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一优俘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掀序,春花似錦帆焕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至换吧,卻和暖如春浑娜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背式散。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工筋遭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暴拄。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓漓滔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乖篷。 傳聞我的和親對象是個殘疾皇子响驴,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容