【转】刨根究底字符编码之十二——UTF-8究竟是怎么编码的
UTF-8究竟是怎么編碼的
1.
UTF-8編碼是Unicode字符集的一種字符編碼方式(CEF),其特點是使用變長字節數(即變長碼元序列或稱變寬碼元序列)來編碼。目前一般是1到4個字節,當然,也可以更長。
為什么要變長呢?這可以理解為按需分配,比如一個字節足以容納所有的ASCII字符,那何必補一堆0,導致占用更多的字節來存儲呢?
實際上變長編碼有其優勢,也有其劣勢,優勢方面除了上面所講的節省存儲空間之外,還有就是自動糾錯性能好、利于傳輸、擴展性強,而劣勢方面主要是由于字符的編碼字節數不固定導致不利于程序內部處理,比如導致正則表達式檢索的復雜度大為增加;而UTF-32這樣的等長碼元序列(即等寬碼元序列)的編碼方式就比較適合程序處理,當然,缺點是比較耗費存儲空間。
2.
那UTF-8究竟是怎么編碼的呢?也就是說其編碼算法是什么?
UTF-8編碼最短的為一個字節、最長的目前為四個字節,從首字節就可以判斷一個UTF-8編碼有幾個字節:
- 如果首字節以0開頭,肯定是單字節編碼(即單個單字節碼元);
- 如果首字節以110開頭,肯定是雙字節編碼(即由兩個單字節碼元所組成的雙碼元序列);
- 如果首字節以1110開頭,肯定是三字節編碼(即由三個單字節碼元所組成的三碼元序列),以此類推。
另外,UTF-8編碼中,除了單字節編碼外,由多個單字節碼元所組成的多字節編碼其首字節以外的后續字節均以10開頭(以區別于單字節編碼以及多字節編碼的首字節)。
0、110、1110以及10相當于UTF-8編碼中各個字節的前綴,因此稱之為前綴碼。其中,前綴碼110、1110及10中的0,是前綴碼中的終結標志。
UTF-8編碼中的前綴碼起到了很好的區分和標識的作用:
- 當解碼程序讀取到一個字節的首位為0,表示這是一個單字節編碼的ASCII字符;
- 當讀取到一個字節的首位為1,表示這是一個非ASCII字符的多字節編碼字符中的某個字節(可能是首字節,也可能是后續字節),接下來若繼續讀取到一個1,則確定為首字節,再繼續讀取直到遇見終結標志0為止,讀取了幾個1,就表示該字符為幾個字節的編碼;
- 當讀取到一個字節的首位為1,緊接著讀取到一個終結標志0,則該字節顯然是非ASCII字符的后續字節(即非首字節)。
(笨笨阿林原創文章,轉載請注明出處)
3.
所以,1~4字節的UTF-8編碼看起來分別是這樣的:
單字節可編碼的Unicode碼點值范圍十六進制為0x0000 ~ 0x007F,十進制為0 ~ 127;
雙字節可編碼的Unicode碼點值范圍十六進制為0x0080 ~ 0x07FF,十進制為128 ~ 2047;
三字節可編碼的Unicode碼點值范圍十六進制為0x0800 ~ 0xFFFF,十進制為2048 ~ 65535;
四字節可編碼的Unicode碼點值范圍十六進制為0x10000 ~ 0x1FFFFF,十進制為65536 ~ 2097151(目前Unicode字符集碼點編號的最大值為0x10FFFF,實際尚未編號到0x1FFFFF;這說明作為變長字節數的UTF-8編碼其未來擴展性非常強,即便目前的四字節編碼也還有大量編碼空間未被使用,更不論還可擴展為五字節、六字節……)。
(笨笨阿林原創文章,轉載請注明出處)
4.
上述Unicode碼點值范圍中十進制值127、2047、65535、2097151這幾個臨界值是怎么來的呢?
因為UTF-8編碼中的每個字節中都含有起到區分和標識之用的前綴碼0、110、1110以及10之一,所以1~4個字節的UTF-8編碼其實際有效位數分別為8-1=7位(2^7-1=127)、16-5=11位(2^11-1=2047)、24-8=16位(2^16-1=65535)、32-11=21位(2^21-1=2097151),如下表所示:
注:上圖中的Unicode range為Unicode碼點值范圍(也就是Unicode碼點編號范圍),Hex為16進制,Binary為二進制;Encoded bytes為UTF-8編碼中各字節的編碼方式(即編碼算法),其中,x代表Unicode二進制碼點值的單字節或低字節中的低7位或8位、y代表兩字節碼點值的高字節中的低3位或8位以及三字節碼點值的中字節中的8位、z代表三字節碼點值的高字節中的低5位。
因此,UTF-8編碼的算法簡單地來概括就是:首先確定UTF-8編碼中各個字節的前綴碼;之后再將UTF-8編碼中各個字節除了前綴碼所占用之外的位,依次分配給Unicode字符碼點值二進制中各個位的值。換言之,就是用Unicode字符碼點值二進制中各個位的值,依次填充UTF-8編碼中的各個字節除了前綴碼所占用之外的位。
5.
由于ASCII字符的UTF-8編碼使用單字節,而且和ASCII編碼一模一樣,這樣所有原先使用ASCII編碼的文檔就可以直接解碼了,無需進行任何轉換,實現了完全兼容。考慮到計算機世界里的英文文檔數量之多,這一點意義重大。
而對于其他非ASCII字符,則使用2~4個字節的編碼來表示。其中,首字節中前置的“1”的個數代表該字符編碼的字節數(如110代表兩個字節、1110代表三個字節,以此類推),非首字節之外的剩余后續字節的前兩位始終是10,這樣就不會與ASCII字符編碼(“0”開頭)以及非ASCII字符的首字節編碼(110或1110等至少兩個“1”開頭)相沖突。
例如,假設某個字符的首字節是1110yyyy,前置有三個1,說明該字符編碼總共有三個字節,必須和后面兩個以10開頭的字節結合才能正確解碼該字符。
6.
由此可知,UTF-8編碼設計得非常精巧,雖說不上完美無瑕,但若與后文將要介紹的UTF-16、UTF-32以及前文介紹過的那些ANSI編碼相比較,對于其精巧設計將體會得更為深切透徹。因此,UTF-8越來越得到全球一致認可,大有一統字符編碼之勢。
(笨笨阿林原創文章,轉載請注明出處)
(未完待續)
【預告:本系列文章下一篇將重點介紹UTF-16編碼,敬請關注!】
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【转】刨根究底字符编码之十二——UTF-8究竟是怎么编码的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】ABP源码分析三十一:ABP.Au
- 下一篇: Knockout学习笔记之二($root