UTF-8爲何會比UTF-16浪費?

And the Lord spake, saying, "First shalt thou take out the Holy Pin. Then, shalt thou count to three. No more. No less. Three shalt be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, nor either count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then, lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who, being naughty in My sight, shall snuff it."

上帝說:『首先取下栓,而後很少很多數到三。應該數到三,你數到的數字是三。你除了數到三,既不要數到四,也不要數到二,五是數多了。「三」一旦被數到,成爲被數到的第三個數字,就高高的向敵人扔出安提拉之神聖手榴彈,阿門。』
—— 巨蟒與聖盃 Monty Python and the Holy Grail (1975)less

UTF-8的來歷

clipboard.png

UTF-8的規範裏充斥着這樣神祕的句子:「第一個位元組由110開始,接着的位元組由10開始」,「第一個位元組由1110開始,接着的位元組由10開始」。spa

那麼這究竟是什麼意思呢?爲何要這麼作呢?設計

咱們先從二進制提及。咱們都知道,一個字節是由8個二進制位構成的,最小就是0000 0000,最大就是1111 1111。那麼一個字節所能表示的最多字符數就是28次方,也就是256。對於26個英文字母來講,大小寫全算上就是52個,再加上10個阿拉伯數字,62個字符,用能夠表達256個不一樣字符的一個字節來存儲是足夠了。3d

可是,咱們中國的經常使用漢字就有3000多個,用一個只能表達256個字符的字節顯然是不夠存儲的。至少也須要有2個字節,1個字節是8個二進制位,2個字節就是16個二進制位,最多能夠表達216次方,也就是256*256=6553665536個字符足夠容納全部中國的漢字,外帶日語、韓語、阿拉伯語、稀其古怪語等等各類各樣的字符。因此這樣就產生了Unicode,由於它用2字節表示字符,因此更嚴格來說應該叫UCS-2,後來由於怪字符太多,2字節都不夠用了,因此又搞出來了一個4字節表示的方法,稱做UCS-4。不過如今對絕大多數人來說UCS-2已是足夠了。code

Unicode原本是一個好東西,用2字節表示65536種字符,全人類皆大歡喜的事情。可是恰恰有一幫子西洋人,非要認爲這個東西是一種浪費,說咱們英文就最多隻須要26個字母就夠了,1個字節就夠了,爲何要浪費2字節呢?好比說字母A就是0100 0001,這一個字節就夠了的東西,你弄2字節,非要在前面加800000 0000 0100 0001,這不是浪費嗎?咱們就偏要用1字節表示英文。blog

好吧,咱們全人類只好作妥協,規定每一個字節,只要看見0打頭的,就知道這是英文字母,這確定不是漢字,只有看見1開頭的,才認爲這是漢字。three

可是咱們漢字用1個字節表示不下,那好辦,用21開頭的字符表示1個漢字。這樣原本16個二進制位,減去2個開頭的1,只剩下14個二進制位了,214次方就是16384個字符,對於中文來說,也是足夠用了。可是無奈他們仍是想表達65536種字符,那怎麼辦呢?就須要3個字節才能容納得下了,因而UTF-8粉墨登場。ip

首先,首位爲0的字符被佔了,只要遇到0開頭的字符,就知道這是一個1字節的字符,沒必要再日後數了,直接拿來用就能夠,最多表示128種字符,從0000 00000111 1111,也就是從0127get

接下來的事情就比較蹊蹺了。咱們怎麼用1開頭的字符既表示2字節,又表示3字節呢?假設咱們只判斷首位的1,這顯然是不行的,沒有辦法區分,因此咱們能夠用10或者11開頭的字符來表示2字節,可是3字節又該以什麼開頭?或者能夠用10開頭表示2字節,用11開頭表示3字節?那麼4字節的字符未來又該怎麼辦?也許咱們能夠用110開頭表示3字節,用111開頭表示4字節?那麼5字節6字節呢?彷佛咱們看到了一個規律:前面的1越多,表明字節數越多。it

這時候,看一下咱們的第一種方案:用10開頭表示2字節,那麼咱們的一個字符將是

10xx xxxx 10xx xxxx

110表示3字節,那麼一個3字節的字符將是:

110x xxxx 110x xxxx 110x xxxx

這樣無疑是能區分得開的。可是4字節怎麼辦?

1110 xxxx 1110 xxxx 1110 xxxx 1110 xxxx

嗎?這樣也能區分開,但彷佛有點浪費。由於每一個字節的前半扇都被無用的位佔滿了,真正有意義的只有後面一半。

或者咱們乾脆這樣作得了,咱們來設計方案二:爲了節省起見,全部後面的字符,咱們通通都以10開頭,只要碰見10咱們就知道它只是整個字符流的一部分,它確定不是開頭,可是10這個開頭已經被咱們剛剛方案一的2字節字符佔用了,怎麼辦?好辦,把2字節字符的開頭從10改爲110,這樣它就確定不會和10衝突了。因而2字節字符變成

110x xxxx 10xx xxxx

再日後順推,3字節字符變成

1110 xxxx 10xx xxxx 10xx xxxx

4字節字符變成

1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx

好像比剛纔的方案一有所節省呢!而且還帶來了額外的好處:若是我沒有見到前面的110或者1110開頭的字節,而直接見到了10開頭的字節,毫無疑問地能夠確定我遇到的不是一個完整字符的開頭,我能夠直接忽略這個錯誤的字節,而直接找下一個正確字符的開頭。

這個改良以後的方案二就是UTF-8

UTF-8表示的字符數

如今,咱們來算一下在UTF-8方案裏,每一種字節能夠表示多少種字符。

1字節的字符,以0開頭的,0xxx xxxx,後面7個有效位,27次方,最多能夠表示128種字符。

2字節的字符,110x xxxx 10xx xxxx,數一數,11x,因此是211次方,210次方是102411次方就是2048,很不幸,只能表示2048種字符,而咱們的經常使用漢字就有3000多個,看來在這一區是放不下了,只好挪到3字節。

3字節的字符,1110 xxxx 10xx xxxx 10xx xxxx,數一數,16x216次方,最多能夠表示65536個字符,因此咱們的漢字就放在這一區,因此在UTF-8方案裏咱們的漢字都是以3個字節表示的。

因此這也就是這一張表的來歷:

clipboard.png

UTF-8和UTF-16

那麼UTF-88是從哪兒來的呢?它的意思就是說咱們以28次方爲一個字節,爲一個最小單元。那麼若是咱們以216次方爲一個最小單元,這就變成了UTF-16,它的規則和UTF-8相同,惟一不一樣的是它最小也要用162進制位表示一個字符,而162進制位直接能夠表示65536種字符,因此在UTF-16方案裏,咱們漢字直接就能夠如英文同樣被冠冕堂皇地放在第1區了,也就是說,和英文具備同等的身份,都佔用162進制位,也就至關於UTF-8裏的2字節哦,看,這樣一來,若是咱們用UTF-16來存儲英文的話,會形成浪費,由於英文在UTF-8裏只佔1字節,而在UTF-16裏要佔2字節,可是若是咱們用UTF-16來存儲中文的話,不但不浪費,反而還節省了呢!由於咱們的中文在UTF-8裏要佔用3字節,而在UTF-16裏只佔用2字節,節省了33%之多呢!

相關文章
相關標籤/搜索