刨根究底字符編碼之十二——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代表三個字節,以此類推),非首字節之外的剩余字節的高2位始終是10,這樣就不會與ASCII字符編碼以及非ASCII字符的首字節編碼相沖突。

例如,假設某個字符的首字節是1110yyyy,前置有三個1,說明該字符編碼總共有三個字節,必須和后面兩個以10開頭的字節結合才能正確解碼該字符。

 

6.

由此可知,UTF-8編碼設計得非常精巧,雖說不上完美無缺,但若與后文將要介紹的UTF-16、UTF-32以及前文介紹過的那些ANSI編碼相比較,對於其精巧設計將體會得更為深切透徹。因此,UTF-8越來越得到全球一致認可,大有一統字符編碼之勢

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

(未完待續)

 

本系列文章上一篇為:刨根究底字符編碼之十一——UTF-8編碼方式與字節序標記 

預告:本系列文章下一篇將重點介紹UTF-16編碼,敬請關注!】

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM