编码(2)从字节理解Unicode(UTF8/UTF16)
https://www.cnblogs.com/zizifn/p/4716712.html
從字節(jié)理解Unicode(UTF8/UTF16)
?
如果你不知道或者不了解什么是Unicode/UTF8/UTF16,請詳細(xì)閱讀這篇文章(這也是這篇博文的先決條件):
學(xué)點編碼知識又不會死:Unicode的流言終結(jié)者和編碼大揭秘
? ?
但是如果你看完以上文章后,疑惑為什么一個Unicode:0x4F60(對應(yīng)漢字是"你")會在UTF8下占用3個字節(jié)的存儲空間。
按照排列組合2個字節(jié)完全可以存儲多數(shù)unicode字符,明顯字符"你"(0x4F60)是在2個字節(jié)最大可能范圍內(nèi)(0xFFFF)。
但是為什么UTF8卻使用3個字節(jié)存儲字符"你"呢?這樣不是明顯浪費存儲空間嗎?
? ?
答案不僅是UTF8沒有浪費存儲空間,而且UTF8還是一個精美的設(shè)計,至少在我看來。
? ?
Unicode/UTF8/UTF16
雖然你或許可能知道什么UTF8,但是我還是要簡單介紹下什么是UTF8?什么是UTF16?什么是unicode?
? ?
看完我開頭推薦的那篇詳細(xì)的博文(學(xué)點編碼知識又不會死:Unicode的流言終結(jié)者和編碼大揭秘)。你應(yīng)該知道unicode是一種索引表,它規(guī)定了任何字符的code。比如:字符"你"就是0x4F60,在整個宇宙(你確定是整個宇宙?)的任何地方,只要你用的是unicode,那么"你"的unicode就是"0x4F60"。
? ?
所以Unicode并不關(guān)心世界上有多少字符,如果你想把一個字符放入Unicode中,那么請告訴我你要放的是什么字符?那么Unicode會給你個索引號碼?比如:漢字"你"就是"0x4F60"。也就是假如有一天人類統(tǒng)一了"三體星人"(可惜的是三體星已經(jīng)被摧毀了。。),我們也可以把三體星文加入到Unicode中。
? ?
當(dāng)然Unicode同樣不關(guān)心你怎么實現(xiàn),你怎么把字符編碼成字節(jié)?所以unicode并不知道字符"你"占用幾個字節(jié)。這時候就是UTF(Unicode Transformation Formats)來規(guī)定unicode字符該如何存儲,占用幾個字節(jié)?
? ?
總而言之:
Unicode定義世界每個字符的索引值。
UTF8/UTF16實現(xiàn)Unicode的標(biāo)準(zhǔn),把字符存儲到存儲介質(zhì)中。
? ?
從字節(jié)角度看UTF8
我們知道存儲字節(jié)多少只和UTF有關(guān),那么我們先看UTF8一張表.詳情請查看wikipedia的介紹?。當(dāng)然你看百度百科也是可以的。
? ?
| 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 |
根據(jù)這張表,我們可以知道0x4F60(字符"你")是在范圍(0x0800-0xFFFF),所以在UF8下需要3個字節(jié)來存儲。
? ?
下面讓例子來闡述為什么需要3個字節(jié)?首先,先看下這3個字節(jié)存放的是什么?
在windows新建一個txt,寫入字符"UTF8你"(加入UTF8是為了有個基準(zhǔn)線查看"你"的真實字節(jié)值),然后另存為UTF8編碼。用notepad++(需要裝HEX-editor插件)或者Binary Viewer,查看"你"在UTF8下的16進(jìn)制值。
? ?
? ?
我們知道字符"UTF8"16進(jìn)制就是他們的ASNI碼"0x55,0x54,0x46,0x38".那么字符"你"在UTF8下3個字節(jié)的值是"0xE4/0xBD/0xA0".
? ?
"0xE4"-->"11100100".
"0xBD"-->"10111101".
"0xA0"-->"10100000".
查看上面UTF8的表,表給出給出每個字節(jié) 的前幾個固定的二進(jìn)制數(shù)。
比如3個字節(jié)的Unicode會用到這個格式:"1110xxxx 10xxxxxx 10xxxxxx"來存儲字符,對應(yīng)到字符"你"就是"11100100?/10111101/ 10100000"。
拿出紅色標(biāo)注的部分"0100 111101 100000",轉(zhuǎn)換成16進(jìn)制就是"0x4F60"也就對應(yīng)的是Unicode字符"你"。
? ?
現(xiàn)在我們可以知道UTF8固定每個字節(jié)的前面幾位二進(jìn)制值,然后用其他的位來表示字符。但是為什么UTF8的設(shè)計者們要這樣設(shè)計呢?
? ?
我想這是UTF8為了兼容ASNI所要付出的代價,請查看上表,UTF8下是完全兼容asni,也就是asni標(biāo)準(zhǔn)的下的文檔,在UTF8下顯示完全不是問題(因為ASNI存儲字節(jié)值和UTF8是一樣的)。字符都是一個一個字節(jié)存儲的,UTF8肯定是一個一個字節(jié)的讀取,那么UTF8怎么在完全兼容ASNI前提下,是怎么知道某個字符是需要額外字節(jié)信息的?UTF8只有固定前幾位二進(jìn)制來決定這個字符需要以后的幾個字節(jié),又因為為了兼容ASNI,所以額外字節(jié)也需要固定前2位"10xxxxxx",來決定這個字節(jié)值不是代表ASNI字符。ASNI的格式是“0xxxxxxx”。
? ?
另外,你也完全可以自己實現(xiàn)一個標(biāo)準(zhǔn)來解釋Unicode,比如就叫做UTF9吧,只要你能完全解釋Unicode。
實際上是有UTF7,UTF8,UTF16,UTF32的。
? ?
從字節(jié)角度看UTF16
同樣的,我們把txt:"UTF8你"另存為UTF16編碼(windows下unicode編碼就是指UTF16)。
? ?
UTF16下的每個字符需要是2個或者4個字節(jié)。
字符"UTF8"在UTF16下就是"0x55/0x0054/0x0045/0x0038",那為什么圖片中是0x5500呢?這涉及到高字節(jié)序和低字節(jié)序。開頭的那篇文章也有介紹。字節(jié)序僅僅就是先把字符的高位或者低位先放入存儲的而已。
? ?
比如字符"你""0x4F60",第一個字節(jié)是"4F"是"高位",第二個字節(jié)是"60"是"低位".
稍微解釋下為什么左邊是高位,玩笑話就是想想你的銀行賬戶當(dāng)然是左面數(shù)值多才有意義啊。
那么按照"低字節(jié)序" "0x4F60"就被存儲為"60 4F"拉。在intel CPU下默認(rèn)是"低字節(jié)序"。
?
在UTF16下,存儲的字節(jié)值和unicode是一一對應(yīng)的。但是UTF16顯示英文(asni)就浪費一個字節(jié)。所以英文國家用UTF8的編碼比較多。反之其他國家用UTF16的較多。
? ??
字節(jié)順序標(biāo)記(BOM)
不知道你有沒有注意到,在UTF16下的這張圖,地址第0,第1位是"FF FE"
?
?
這就是BOM,通過FF FE或者FE FF來告訴解釋器是那種字節(jié)序。
那么你也許會問,為什么UTF8沒有字節(jié)序呢?那是因為UTF8是以字節(jié)為單位,一個一個字節(jié)讀取。UTF16是以字為單位,一個一個字符(2個字節(jié)或者4個字節(jié))讀取,這樣就會涉及先讀取第一個或者第二個字節(jié)的情況。
? ?
希望這篇文章從存儲字節(jié)角度看UTF8和UTF16會為給你帶來不一樣的感覺。
?
總結(jié)
以上是生活随笔為你收集整理的编码(2)从字节理解Unicode(UTF8/UTF16)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「整理」公式专区
- 下一篇: CentOS7加入windows 200