從字節理解Unicode(UTF8/UTF16)

如果你不知道或者不瞭解什麼是Unicode/UTF8/UTF16,請詳細閱讀這篇文章(這也是這篇博文的先決條件):

學點編碼知識又不會死:Unicode的流言終結者和編碼大揭祕

   

但是如果你看完以上文章後,疑惑爲什麼一個Unicode:0x4F60(對應漢字是"你")會在UTF8下佔用3個字節的存儲空間。

按照排列組合2個字節完全可以存儲多數unicode字符,明顯字符"你"(0x4F60)是在2個字節最大可能範圍內(0xFFFF)。

但是爲什麼UTF8卻使用3個字節存儲字符"你"呢?這樣不是明顯浪費存儲空間嗎?

   

答案不僅是UTF8沒有浪費存儲空間,而且UTF8還是一個精美的設計,至少在我看來。

   

Unicode/UTF8/UTF16

雖然你或許可能知道什麼UTF8,但是我還是要簡單介紹下什麼是UTF8?什麼是UTF16?什麼是unicode?

   

看完我開頭推薦的那篇詳細的博文(學點編碼知識又不會死:Unicode的流言終結者和編碼大揭祕)。你應該知道unicode是一種索引表,它規定了任何字符的code。比如:字符"你"就是0x4F60,在整個宇宙(你確定是整個宇宙?)的任何地方,只要你用的是unicode,那麼"你"的unicode就是"0x4F60"。

   

所以Unicode並不關心世界上有多少字符,如果你想把一個字符放入Unicode中,那麼請告訴我你要放的是什麼字符?那麼Unicode會給你個索引號碼?比如:漢字"你"就是"0x4F60"。也就是假如有一天人類統一了"三體星人"(可惜的是三體星已經被摧毀了。。),我們也可以把三體星文加入到Unicode中。

   

當然Unicode同樣不關心你怎麼實現,你怎麼把字符編碼成字節?所以unicode並不知道字符"你"佔用幾個字節。這時候就是UTF(Unicode Transformation Formats)來規定unicode字符該如何存儲,佔用幾個字節?

   

總而言之:

Unicode定義世界每個字符的索引值。

UTF8/UTF16實現Unicode的標準,把字符存儲到存儲介質中。

   

從字節角度看UTF8

我們知道存儲字節多少隻和UTF有關,那麼我們先看UTF8一張表.詳情請查看wikipedia的介紹 。當然你看百度百科也是可以的。

   

Bits of

code point

First

code point

Last

code point

Bytes in

sequence

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

根據這張表,我們可以知道0x4F60(字符"你")是在範圍(0x0800-0xFFFF),所以在UF8下需要3個字節來存儲。

   

下面讓例子來闡述爲什麼需要3個字節?首先,先看下這3個字節存放的是什麼?

在windows新建一個txt,寫入字符"UTF8你"(加入UTF8是爲了有個基準線查看"你"的真實字節值),然後另存爲UTF8編碼。用notepad++(需要裝HEX-editor插件)或者Binary Viewer,查看"你"在UTF8下的16進制值。

   

   

我們知道字符"UTF8"16進制就是他們的ASNI碼"0x55,0x54,0x46,0x38".那麼字符"你"在UTF8下3個字節的值是"0xE4/0xBD/0xA0".

   

"0xE4"-->"11100100".

"0xBD"-->"10111101".

"0xA0"-->"10100000".

查看上面UTF8的表,表給出給出每個字節 的前幾個固定的二進制數。

比如3個字節的Unicode會用到這個格式:"1110xxxx 10xxxxxx 10xxxxxx"來存儲字符,對應到字符"你"就是"11100100 /10111101/ 10100000"。

拿出紅色標註的部分"0100 111101 100000",轉換成16進制就是"0x4F60"也就對應的是Unicode字符"你"。

   

現在我們可以知道UTF8固定每個字節的前面幾位二進制值,然後用其他的位來表示字符。但是爲什麼UTF8的設計者們要這樣設計呢?

   

我想這是UTF8爲了兼容ASNI所要付出的代價,請查看上表,UTF8下是完全兼容asni,也就是asni標準的下的文檔,在UTF8下顯示完全不是問題(因爲ASNI存儲字節值和UTF8是一樣的)。字符都是一個一個字節存儲的,UTF8肯定是一個一個字節的讀取,那麼UTF8怎麼在完全兼容ASNI前提下,是怎麼知道某個字符是需要額外字節信息的?UTF8只有固定前幾位二進制來決定這個字符需要以後的幾個字節,又因爲爲了兼容ASNI,所以額外字節也需要固定前2位"10xxxxxx",來決定這個字節值不是代表ASNI字符。ASNI的格式是“0xxxxxxx”。

   

另外,你也完全可以自己實現一個標準來解釋Unicode,比如就叫做UTF9吧,只要你能完全解釋Unicode。

實際上是有UTF7,UTF8,UTF16,UTF32的。

   

從字節角度看UTF16

同樣的,我們把txt:"UTF8你"另存爲UTF16編碼(windows下unicode編碼就是指UTF16)。

   

UTF16下的每個字符需要是2個或者4個字節。

字符"UTF8"在UTF16下就是"0x55/0x0054/0x0045/0x0038",那爲什麼圖片中是0x5500呢?這涉及到高字節序和低字節序。開頭的那篇文章也有介紹。字節序僅僅就是先把字符的高位或者低位先放入存儲的而已。

  1. 高字節序,高位字節被存在前面
  2. 低字節序,低位字節被存在前面

   

比如字符"你""0x4F60",第一個字節是"4F"是"高位",第二個字節是"60"是"低位".

稍微解釋下爲什麼左邊是高位,玩笑話就是想想你的銀行賬戶當然是左面數值多才有意義啊。

那麼按照"低字節序" "0x4F60"就被存儲爲"60 4F"拉。在intel CPU下默認是"低字節序"。

 

在UTF16下,存儲的字節值和unicode是一一對應的。但是UTF16顯示英文(asni)就浪費一個字節。所以英文國家用UTF8的編碼比較多。反之其他國家用UTF16的較多。

    

字節順序標記(BOM)

不知道你有沒有注意到,在UTF16下的這張圖,地址第0,第1位是"FF FE"

 

 

這就是BOM,通過FF FE或者FE FF來告訴解釋器是那種字節序。

那麼你也許會問,爲什麼UTF8沒有字節序呢?那是因爲UTF8是以字節爲單位,一個一個字節讀取。UTF16是以字爲單位,一個一個字符(2個字節或者4個字節)讀取,這樣就會涉及先讀取第一個或者第二個字節的情況。

   

希望這篇文章從存儲字節角度看UTF8和UTF16會爲給你帶來不一樣的感覺。

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章