摘自:https://zh.wikipedia.org/wiki/UTF-8,個別地方有幾個特殊符號沒被處理,顯示成層疊狀態。php
UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,也是一種前綴碼。它能夠用來表示Unicode標準中的任何字符,且其編碼中的第一個字節仍與ASCII兼容,這使得原來處理ASCII字符的軟件無須或只須作少部分修改,便可繼續使用。所以,它逐漸成爲電子郵件、網頁及其餘存儲或發送文字的應用中,優先採用的編碼。html
UTF-8使用一至六個字節爲每一個字符編碼(儘管如此,2003年11月UTF-8被RFC 3629從新規範,只能使用原來Unicode定義的區域,U+0000到U+10FFFF,也就是說最多四個字節):java
對上述說起的第四種字符而言,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編碼系統。但有關的編碼器實際上歷來沒有實現這方面的規格。網絡
目前有好幾份關於UTF-8詳細規格的文件,但這些文件在定義上有些許的不一樣:
它們替換了如下那些被淘汰的定義:
事實上,全部定義的基本原理都是相同的,它們之間最主要的不一樣是支持的字符範圍及無效輸入的處理方法。
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 |
例如,希伯來語字母aleph(א)的Unicode代碼是U+05D0,按照如下方法改爲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編碼中的任意字節,根據第一位,可判斷是否爲ASCII字符;根據前二位,可判斷該字節是否爲一個字符編碼的第一個字節;根據前四位(若是前兩位均爲1),可肯定該字節爲字符編碼的第一個字節,而且可判斷對應的字符由幾個字節表示;根據前五位(若是前四位爲1),可判斷編碼是否有錯誤或數據傳輸過程當中是否有錯誤。
UTF-8的設計有如下的多字符組序列的特質:
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是UNICODE的一種變長度的編碼表達方式《通常UNICODE爲雙位元組(指UCS2)》,它由肯·湯普遜(Ken Thompson)於1992年建立,如今已經標準化爲RFC 3629。UTF-8就是以8位爲單元對UCS進行編碼,而UTF-8不使用大尾序和小尾序的形式,每一個使用UTF-8存儲的字符,除了第一個字節外,其他字節的頭兩個比特都是以"10"開始,使文字處理器可以較快地找出每一個字符的開始位置。
但爲了與之前的ASCII碼兼容(ASCII爲一個字節),所以UTF-8選擇了使用可變長度字節來存儲Unicode:
碼點的位數 | 碼點起值 | 碼點終值 | 字節序列 | 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字母繼續使用1字節存儲,重音文字、希臘字母或西裏爾字母等使用2字節來存儲,而經常使用的漢字就要使用3字節。輔助平面字符則使用4字節。
在UTF-8文件的開首,不少時都放置一個U+FEFF字符(UTF-8以EF,BB,BF表明),以顯示這個文本文件是以UTF-8編碼。
UTF-8 | |
---|---|
最小碼位 | 0000 |
最大碼位 | 10FFFF |
每字節所佔位數 | 8 bits |
Byte order | N/A |
每一個字符最小字節數 | 1 |
每一個字符最大字節數 | 4 |
整體來講,在Unicode字符串中不可能由碼點數量決定顯示它所須要的長度,或者顯示字符串以後在文本緩衝區中光標應該放置的位置;組合字符、變寬字體、不可打印字符和從右至左的文字都是其歸因。
因此儘管在UTF-8字符串中字符數量與碼點數量的關係比UTF-32更爲複雜,在實際中不多會遇到有不一樣的情形。
更詳細的說,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)這樣的方塊文字。
在數據庫系統MySQL或MariaDB中有多種字符集,其中utf8_unicode_ci和utf8_general_ci是最經常使用的,可是utf8_general_ci對某些語言的支持有一些小問題,若是能夠接受,那最好使用utf8_general_ci,由於它速度快。不然,請使用較爲精確的utf8_unicode_ci,不過速度會慢一些。
雖然不是標準,但許多Windows程序(包括Windows記事本)在UTF-8編碼的文件的開首加入一段字節串EF BB BF
。這是字節順序記號U+FEFF
的UTF-8編碼結果。對於沒有預期要處理UTF-8的文本編輯器和瀏覽器會顯示成ISO-8859-1字符串
。
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程序語言在經過InputStreamReader
和OutputStreamWriter
讀取和寫入串的時候支持標準UTF-8。可是,Java也支持一種非標準的變體UTF-8,供對象的序列化,Java本地界面和在class文件中的嵌入常數時使用的modified 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操做系統使用正式分解萬國碼(canonically decomposed Unicode),在文件系統中使用UTF-8編碼進行文件命名,這作法一般被稱爲UTF-8-MAC。正式分解萬國碼中,預組合字符是被禁止使用的,必須以組合字符替換。
這種方法使分類變得很是簡單,可是會搞混那些使用預組合字符爲標準、組合字符用來顯示特殊字符的軟件。Mac系統的這種NFD數據是萬國碼規範化(Unicode normalization)的一種格式。而其餘系統,包括Windows和Linux,使用萬國碼規範的NFC形式,也是W3C標準使用的形式。因此一般NFD數據必須轉換成NFC才能被其餘平臺或者網絡使用。
蘋果開發者專區有關於此問題的討論:Apple Q&A 1173。