UTF-8

摘自:https://zh.wikipedia.org/wiki/UTF-8,個別地方有幾個特殊符號沒被處理,顯示成層疊狀態。php

維基百科,自由的百科全書

UTF-88-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,也是一種前綴碼。它能夠用來表示Unicode標準中的任何字符,且其編碼中的第一個字節仍與ASCII兼容,這使得原來處理ASCII字符的軟件無須或只須作少部分修改,便可繼續使用。所以,它逐漸成爲電子郵件網頁及其餘存儲或發送文字的應用中,優先採用的編碼。html

UTF-8使用一至六個字節爲每一個字符編碼(儘管如此,2003年11月UTF-8被RFC 3629從新規範,只能使用原來Unicode定義的區域,U+0000到U+10FFFF,也就是說最多四個字節):java

  1. 128個US-ASCII字符只需一個字節編碼(Unicode範圍由U+0000至U+007F)。
  2. 帶有附加符號拉丁文希臘文西裏爾字母亞美尼亞語希伯來文阿拉伯文敘利亞文它拿字母則須要兩個字節編碼(Unicode範圍由U+0080至U+07FF)。
  3. 其餘基本多文種平面(BMP)中的字符(這包含了大部分經常使用字,如大部分的漢字)使用三個字節編碼(Unicode範圍由U+0800至U+FFFF)。
  4. 其餘極少使用的Unicode 輔助平面的字符使用四至六字節編碼(Unicode範圍由U+10000至U+1FFFFF使用四字節,Unicode範圍由U+200000至U+3FFFFFF使用五字節,Unicode範圍由U+4000000至U+7FFFFFFF使用六字節)。

對上述說起的第四種字符而言,UTF-8使用四至六個字節來編碼彷佛太耗費資源了。但UTF-8對全部經常使用的字符均可以用三個字節表示,並且它的另外一種選擇,UTF-16編碼,對前述的第四種字符一樣須要四個字節來編碼,因此要決定UTF-8或UTF-16哪一種編碼比較有效率,還要視所使用的字符的分佈範圍而定。不過,若是使用一些傳統的壓縮系統,好比DEFLATE,則這些不一樣編碼系統間的的差別就變得微不足道了。若顧及傳統壓縮算法在壓縮較短文字上的效果不大,能夠考慮使用Unicode標準壓縮格式(SCSU)。linux

互聯網工程工做小組(IETF)要求全部互聯網協議都必須支持UTF-8編碼[1]互聯網郵件聯盟(IMC)建議全部電子郵件軟件都支持UTF-8編碼。[1]正則表達式

歷史[編輯]

1992年初,爲建立良好的字節串編碼系統以供多字節字符集使用,開始了一個正式的研究。ISO/IEC 10646的初稿中有一個非必須的附錄,名爲UTF。當中包含了一個供32比特字符使用的字節串編碼系統。這個編碼方式的性能並不使人滿意,但它提出了將0-127的範圍保留給ASCII以兼容舊系統的概念。算法

1992年7月,X/Open委員會XoJIG開始尋求一個較佳的編碼系統。Unix系統實驗室(USL)的Dave Prosser爲此提出了一個編碼系統的建議。它具有可更快速實現的特性,並引入一項新的改進。其中,7比特ASCII符號只表明原來的意思,全部多字節序列則會包含第8比特的符號,也就是所謂的最高有效比特數據庫

1992年8月,這個建議由IBMX/Open的表明流傳到一些感興趣的團體。與此同時,貝爾實驗室九號項目操做系統工做小組的肯·湯普遜對這編碼系統做出重大的修改,讓編碼能夠自我同步,使得沒必要從字符串的開首讀取,也能找出字符間的分界。1992年9月2日,肯·湯普遜羅勃·派克一塊兒在美國新澤西州一架餐車的餐桌墊上描繪出此設計的要點。接下來的日子,Pike及湯普遜將它實現,並將這編碼系統徹底應用在九號項目當中,及後他將有關成果回饋X/Open。api

1993年1月25-29日的在聖地牙哥舉行的USENIX會議首次正式介紹UTF-8。瀏覽器

自1996年起,微軟CAB(MS Cabinet)規格在UTF-8標準正式落實前就明確允許在任何地方使用UTF-8編碼系統。但有關的編碼器實際上歷來沒有實現這方面的規格。網絡

描述[編輯]

Unicode與UTF-8的轉換

目前有好幾份關於UTF-8詳細規格的文件,但這些文件在定義上有些許的不一樣:

  • RFC 3629 / STD 63(2003),這份文件制定了UTF-8是標準的互聯網協議元素
  • 第四版,The Unicode Standard,§3.9-§3.10(2003)
  • ISO/IEC 10646-1:2000附加文件D(2000)

它們替換了如下那些被淘汰的定義:

  • ISO/IEC 10646-1:1993修正案2/附加文件R(1996)
  • 第二版,The Unicode Standard,附錄A(1996)
  • RFC 2044(1996)
  • RFC 2279(1998)
  • 第三版,The Unicode Standard,§2.3(2000)及勘誤表#1:UTF-8 Shortest Form(2000)
  • Unicode Standard附加文件#27: Unicode 3.1(2001)

事實上,全部定義的基本原理都是相同的,它們之間最主要的不一樣是支持的字符範圍及無效輸入的處理方法。

Unicode字符比特被分區爲數個部分,並分配到UTF-8的字節串中較低的比特的位置。在U+0080的如下字符都使用內含其字符的單字節編碼。這些編碼正好對應7比特的ASCII字符。在其餘狀況,有可能須要多達4個字符組來表示一個字符。這些多字節的最高有效比特會設置成1,以防止與7比特的ASCII字符混淆,並保持標準的字節主導字符串運做順利。

代碼範圍
十六進制
標量值(scalar value)
二進制
UTF-8
二進制十六進制
註釋
000000 - 00007F
128個代碼
00000000 00000000 0zzzzzzz 0zzzzzzz(00-7F) ASCII字符範圍,字節由零開始
七個z 七個z
000080 - 0007FF
1920個代碼
00000000 00000yyy yyzzzzzz 110yyyyy(C0-DF) 10zzzzzz(80-BF) 第一個字節由110開始,接着的字節由10開始
三個y;二個y;六個z 五個y;六個z
000800 - 00D7FF
00E000 - 00FFFF
61440個代碼 [Note 1]
00000000 xxxxyyyy yyzzzzzz 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz 第一個字節由1110開始,接着的字節由10開始
四個x;四個y;二個y;六個z 四個x;六個y;六個z
010000 - 10FFFF
1048576個代碼
000wwwxx xxxxyyyy yyzzzzzz 11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz 將由11110開始,接着的字節由10開始
三個w;二個x;四個x;四個y;二個y;六個z 三個w;六個x;六個y;六個z
Note 1 Unicode在範圍D800-DFFF中不存在任何字符, 基本多文種平面中約定了這個範圍用於UTF-16擴展標識 輔助平面(兩個UTF-16表示一個 輔助平面字符)。固然,任何編碼都是能夠被轉換到這個範圍,但在unicode中他們並不表明任何合法的值。

例如,希伯來語字母aleph(א)的Unicode代碼是U+05D0,按照如下方法改爲UTF-8:

  • 它屬於U+0080到U+07FF區域,這個表說明它使用雙字節,110yyyyy 10zzzzzz.
  • 十六進制的0x05D0換算成二進制就是101-1101-0000.
  • 這11位數按順序放入"y"部分和"z"部分:11010111 10010000.
  • 最後結果就是雙字節,用十六進制寫起來就是0xD7 0x90,這就是這個字符aleph(א)的UTF-8編碼。

因此開始的128個字符(US-ASCII)只需一字節,接下來的1920個字符須要雙字節編碼,包括帶附加符號拉丁字母希臘字母西裏爾字母科普特語字母,亞美尼亞語字母,希伯來文字母和阿拉伯字母的字符。基本多文種平面中其他的字符使用三個字節,剩餘字符使用四個字節。

根據這種方式能夠處理更大數量的字符。原來的規範容許長達6字節的序列,能夠覆蓋到31位(通用字符集原來的極限)。儘管如此,2003年11月UTF-8被RFC 3629從新規範,只能使用原來Unicode定義的區域,U+0000到U+10FFFF。根據這些規範,如下字節值將沒法出如今合法UTF-8序列中:

編碼(二進制 編碼(十六進制 註釋
1100000x C0, C1 過長編碼:雙字節序列的頭字節,但碼點 <= 127
1111111x FE, FF 沒法達到:7或8字節序列的頭字節
111110xx
1111110x
F8, F9, FA, FB, FC, FD 被RFC 3629規範:5或6字節序列的頭字節
11110101
1111011x
F5, F6, F7 被RFC 3629規範:碼點超過10FFFF的頭字節

UTF-8編碼字節含義[編輯]

  • 對於UTF-8編碼中的任意字節B,若是B的第一位爲0,則B獨立的表示一個字符(ASCII碼);
  • 若是B的第一位爲1,第二位爲0,則B爲一個多字節字符中的一個字節(非ASCII字符);
  • 若是B的前兩位爲1,第三位爲0,則B爲兩個字節表示的字符中的第一個字節;
  • 若是B的前三位爲1,第四位爲0,則B爲三個字節表示的字符中的第一個字節;
  • 若是B的前四位爲1,第五位爲0,則B爲四個字節表示的字符中的第一個字節;

所以,對UTF-8編碼中的任意字節,根據第一位,可判斷是否爲ASCII字符;根據前二位,可判斷該字節是否爲一個字符編碼的第一個字節;根據前四位(若是前兩位均爲1),可肯定該字節爲字符編碼的第一個字節,而且可判斷對應的字符由幾個字節表示;根據前五位(若是前四位爲1),可判斷編碼是否有錯誤或數據傳輸過程當中是否有錯誤。

設計UTF-8的理由[編輯]

UTF-8的設計有如下的多字符組序列的特質:

  • 單字節字符的最高有效比特永遠爲0。
  • 多字節序列中的首個字符組的幾個最高有效比特決定了序列的長度。最高有效位爲110的是2字節序列,而1110的是三字節序列,如此類推。
  • 多字節序列中其他的字節中的首兩個最高有效比特爲10

UTF-8的這些特質,保證了一個字符的字節序列不會包含在另外一個字符的字節序列中。這確保了以字節爲基礎的部分字符串比對(sub-string match)方法能夠適用於在文字中搜索字或詞。有些比較舊的可變長度8位編碼(如Shift JIS)沒有這個特質,故字符串比對的算法變得至關複雜。雖然這增長了UTF-8編碼的字符串的信息冗餘,可是利多於弊。另外,數據壓縮並不是Unicode的目的,因此不可混爲一談。即便在發送過程當中有部分字節因錯誤或干擾而徹底丟失,仍是有可能在下一個字符的起點從新同步,令受損範圍受到限制。

另外一方面,因爲其字節序列設計,若是一個疑似爲字符串的序列被驗證爲UTF-8編碼,那麼咱們能夠有把握地說它是UTF-8字符串。一段兩字節隨機序列碰巧爲合法的UTF-8而非ASCII的機率爲32分1。對於三字節序列的機率爲256分1,對更長的序列的機率就更低了。

UTF-8的編碼方式[編輯]

UTF-8是UNICODE的一種變長度的編碼表達方式《通常UNICODE爲雙位元組(指UCS2)》,它由肯·湯普遜Ken Thompson於1992年建立,如今已經標準化爲RFC 3629。UTF-8就是以8位爲單元對UCS進行編碼,而UTF-8不使用大尾序和小尾序的形式,每一個使用UTF-8存儲的字符,除了第一個字節外,其他字節的頭兩個比特都是以"10"開始,使文字處理器可以較快地找出每一個字符的開始位置。

但爲了與之前的ASCII碼兼容(ASCII爲一個字節),所以UTF-8選擇了使用可變長度字節來存儲Unicode:

Unicode 和 UTF-8 之間的轉換關係表 ( x 字符表示碼點佔據的位 )
碼點的位數 碼點起值 碼點終值 字節序列 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
  7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 在ASCII碼的範圍,用一個字節表示,超出ASCII碼的範圍就用字節表示,這就造成了咱們上面看到的UTF-8的表示方法,這様的好處是當UNICODE文件中只有ASCII碼時,存儲的文件都爲一個字節,因此就是普通的ASCII文件無異,讀取的時候也是如此,因此能與之前的ASCII文件兼容。
  • 大於ASCII碼的,就會由上面的第一字節的前幾位表示該unicode字符的長度,好比110xxxxx前三位的二進制表示告訴咱們這是個2BYTE的UNICODE字符;1110xxxx是個三位的UNICODE字符,依此類推;xxx的位置由字符編碼數的二進制表示的位填入。越靠右的x具備越少的特殊意義。只用最短的那個足夠表達一個字符編碼數的多字節串。注意在多字節串中,第一個字節的開頭"1"的數目就是整個串中字節的數目。

ASCII字母繼續使用1字節存儲,重音文字希臘字母西裏爾字母等使用2字節來存儲,而經常使用的漢字就要使用3字節。輔助平面字符則使用4字節。

在UTF-8文件的開首,不少時都放置一個U+FEFF字符(UTF-8以EF,BB,BF表明),以顯示這個文本文件是以UTF-8編碼。

UTF-8的特性[編輯]

UTF-8圖表說明
  UTF-8
最小碼位 0000
最大碼位 10FFFF
每字節所佔位數 8 bits
Byte order N/A
每一個字符最小字節數 1
每一個字符最大字節數 4
  • UCS字符U+0000到U+007F(ASCII)被編碼爲字節0x00到0x7F(ASCII兼容),這也意味着只包含7位ASCII字符的文件在ASCII和UTF-8兩種編碼方式下是同樣的。
  • 全部>U+007F的UCS字符被編碼爲一個多個字節的串,每一個字節都有標記位集。所以,ASCII字節(0x00-0x7F)不可能做爲任何其餘字符的一部分。
  • 表示非ASCII字符的多字節串的第一個字節老是在0xC0到0xFD的範圍裏,並指出這個字符包含多少個字節。多字節串的其他字節都在0x80到0xBF範圍裏,這使得從新同步很是容易,並使編碼無國界,且不多受丟失字節的影響。
  • 能夠編入全部可能的231個UCS代碼
  • UTF-8編碼字符理論上能夠最多到6個字節長,然而16位BMP字符最多隻用到3字節長。
  • Bigendian UCS-4字節串的排列順序是預約的。
  • 字節0xFE和0xFF在UTF-8編碼中從未用到,同時,UTF-8以字節爲編碼單元,它的字節順序在全部系統中都是一様的,沒有字節序的問題,也所以它實際上並不須要BOM
  • 與UTF-16或其餘Unicode編碼相比,對於不支持Unicode和XML的系統,UTF-8更不容易形成問題。

UTF-8編碼的優勢[編輯]

整體來講,在Unicode字符串中不可能由碼點數量決定顯示它所須要的長度,或者顯示字符串以後在文本緩衝區中光標應該放置的位置;組合字符、變寬字體、不可打印字符和從右至左的文字都是其歸因。

因此儘管在UTF-8字符串中字符數量與碼點數量的關係比UTF-32更爲複雜,在實際中不多會遇到有不一樣的情形。

更詳細的說,UTF-8編碼具備如下幾點優勢:

  • ASCII是UTF-8的一個子集。由於一個純ASCII字符串也是一個合法的UTF-8字符串,因此現存的ASCII文本不須要轉換。爲傳統的擴展ASCII字符集設計的軟件一般能夠不經修改或不多修改就能與UTF-8一塊兒使用。
  • 使用標準的面向字節的排序例程對UTF-8排序將產生與基於Unicode代碼點排序相同的結果。(儘管這隻有有限的有用性,由於在任何特定語言或文化下都不太可能有仍可接受的文字排列順序。)
  • UTF-8和UTF-16都是可擴展標記語言文檔的標準編碼。全部其它編碼都必須經過顯式或文本聲明來指定。[2]
  • 任何面向字節字符串搜索算法均可以用於UTF-8的數據(只要輸入僅由完整的UTF-8字符組成)。可是,對於包含字符記數的正則表達式或其它結構必須當心。
  • UTF-8字符串能夠由一個簡單的算法可靠地識別出來。就是,一個字符串在任何其它編碼中表現爲合法的UTF-8的可能性很低,並隨字符串長度增加而減少。舉例說,字符值C0,C1,F5至FF歷來沒有出現。爲了更好的可靠性,可使用正則表達式來統計非法過長和替代值(能夠查看W3 FAQ: Multilingual Forms上的驗證UTF-8字符串的正則表達式)。
  • 與UCS-2的比較:ASCII轉換成UCS-2,在編碼前插入一個0x0。用這些編碼,會含括一些控制符,好比"或 '/',這在UNIX和一些C函數中,將會產生嚴重錯誤。所以能夠確定,UCS-2不適合做爲Unicode的外部編碼,也所以誕生了UTF-8。

UTF-8編碼的缺點[編輯]

編寫不良的解析器[編輯]

一份寫得不好(而且與當前標準的版本不兼容)的UTF-8解析器可能會接受一些不一樣的僞UTF-8表示並將它們轉換到相同的Unicode輸出上。這爲設計用於處理八位表示的校驗例程提供了一種遺漏信息的方式。

不利於正則表達式檢索[編輯]

正則表達式能夠進行不少英文高級的模糊檢索。例如,[a-h]表示a到h間全部字母。

一樣GBK編碼的中文也能夠這樣利用正則表達式,好比在只知道一個字的讀音而不知道怎麼寫的狀況下,也可用正則表達式檢索,由於GBK編碼是按讀音排序的。只是UTF-8不是按讀音排序的,因此會對正則表達式檢索形成不利影響。可是這種使用方式並未考慮中文中的破音字,所以影響不大。Unicode是按部首排序的,所以在只知道一個字的部首而不知道如何發音的狀況下,UTF-8可用正則表達式檢索而GBK不行。

其餘[編輯]

與其餘Unicode編碼相比,特別是UTF-16,在UTF-8中ASCII字符佔用的空間只有一半,但是在一些字符的UTF-8編碼佔用的空間就要多出1/3,特別是中文、日文和韓文(CJK)這樣的方塊文字。

utf8_unicode_ci和utf8_general_ci區別[編輯]

在數據庫系統MySQLMariaDB中有多種字符集,其中utf8_unicode_ci和utf8_general_ci是最經常使用的,可是utf8_general_ci對某些語言的支持有一些小問題,若是能夠接受,那最好使用utf8_general_ci,由於它速度快。不然,請使用較爲精確的utf8_unicode_ci,不過速度會慢一些。

UTF-8的派生物[編輯]

Windows[編輯]

雖然不是標準,但許多Windows程序(包括Windows記事本)在UTF-8編碼的文件的開首加入一段字節串EF BB BF。這是字節順序記號U+FEFF的UTF-8編碼結果。對於沒有預期要處理UTF-8的文本編輯器和瀏覽器會顯示成ISO-8859-1字符串

Posix系統[編輯]

Posix系統明確不建議使用字節序掩碼EF BB BF[2]由於不少文本文件指望以 「#!」開頭指示要運行的程序。Linux系統選擇使用Unicode規範形式Normalization Form C(NFC),即優先使用預組裝字符(precomposed character)而非組合字符序列(combining character sequence)。

2002年9月發佈的Red Hat Linux 8.0纔開始正式把大多數區域設置的默認編碼設爲UTF-8。此前是各類語言的但字節編碼爲主。2004年9月SuSE Linux 9.1開始,缺省編碼遷移爲UTF-8。

字符串處理時,使用UTF-8或locale依賴的多字節編碼情形,比使用C語言wchar_t的寬字符固定寬度編碼,要慢1至2個數量級。[2]

Java[編輯]

在一般用法下,Java程序語言在經過InputStreamReaderOutputStreamWriter讀取和寫入串的時候支持標準UTF-8。可是,Java也支持一種非標準的變體UTF-8,供對象的序列化Java本地界面和在class文件中的嵌入常數時使用的modified UTF-8

變種UTF-8[編輯]

標準和變種的UTF-8有兩個不一樣點。第一,空字符(null character,U+0000)使用雙字節的0xc0 0x80,而不是單字節的0x00。這保證了在已編碼字符串中沒有嵌入空字節。由於C語言等語言程序中,單字節空字符是用來標誌字符串結尾的。當已編碼字符串放到這樣的語言中處理,一個嵌入的空字符將把字符串一刀兩斷。

第二個不一樣點是基本多文種平面以外字符的編碼的方法。在標準UTF-8中,這些字符使用4字節形式編碼,而在改正的UTF-8中,這些字符和UTF-16同樣首先表示爲代理對(surrogate pairs),而後再像CESU-8那樣按照代理對分別編碼。這樣改正的緣由更是微妙。Java中的字符爲16位長,所以一些Unicode字符須要兩個Java字符來表示。語言的這個性質蓋過了Unicode的增補平面的要求。儘管如此,爲了要保持良好的向後兼容、要改變也不容易了。這個改正的編碼系統保證了一個已編碼字符串能夠一次編爲一個UTF-16碼,而不是一次一個Unicode碼點。不幸的是,這也意味着UTF-8中須要4字節的字符在變種UTF-8中變成須要6字節。

由於變種UTF-8並不是UTF-8,因此用戶在交換信息和使用互聯網的時候須要特別注意不要誤把變種UTF-8當成UTF-8數據。

Mac OS X[編輯]

Mac OS X操做系統使用正式分解萬國碼(canonically decomposed Unicode),在文件系統中使用UTF-8編碼進行文件命名,這作法一般被稱爲UTF-8-MAC。正式分解萬國碼中,預組合字符是被禁止使用的,必須以組合字符替換。

這種方法使分類變得很是簡單,可是會搞混那些使用預組合字符爲標準、組合字符用來顯示特殊字符的軟件。Mac系統的這種NFD數據是萬國碼規範化(Unicode normalization)的一種格式。而其餘系統,包括WindowsLinux,使用萬國碼規範的NFC形式,也是W3C標準使用的形式。因此一般NFD數據必須轉換成NFC才能被其餘平臺或者網絡使用。

蘋果開發者專區有關於此問題的討論:Apple Q&A 1173

參考文獻[編輯]

統一碼聯盟出版的書[編輯]

  • The Unicode Standard, Version 5.0, Fifth Edition, The Unicode Consortium, Addison-Wesley Professional,2006年10月27日。ISBN 0-321-48091-0
  • The Unicode Standard, Version 4.0, The Unicode Consortium, Addison-Wesley Professional,2003年8月27日。ISBN 0-321-18578-1

外部連接[編輯]

參見[編輯]

相關文章
相關標籤/搜索