三個(gè)一組還是四個(gè)一組?從 Bytes 到 Unicode 的字節(jié)劃分方法
大家在 Python 開發(fā)過程中,經(jīng)常會(huì)進(jìn)行字符串encode為 Bytes型數(shù)據(jù),或者把 Bytes 型數(shù)據(jù) decode為字符串的操作。例如:圖片我們知道,在 Unicode 編碼中,中文占3個(gè)字節(jié),所以一個(gè)中文字符編碼為 Bytes 型數(shù)據(jù)以后,會(huì)占用3個(gè) Bytes 字符,例如:
- >>> a = '青'
- >>> a.encode()
- b'\xe9\x9d\x92'
- >>> b = '青南'
- >>> b.encode()
- b'\xe9\x9d\x92\xe5\x8d\x97'
注意這里的\xe9需要作為整體來看待,表示一個(gè)16進(jìn)制數(shù)。
所以,當(dāng)我要把 Bytes 型數(shù)據(jù)\xe9\x9d\x92\xe5\x8d\x97 轉(zhuǎn)為字符串時(shí),Python 會(huì)把\xe9\x9d\x92轉(zhuǎn)成青字,把\xe5\x8d\x97轉(zhuǎn)成南字,看起來,似乎是 Python 知道應(yīng)該把每3個(gè) Bytes 符號一組來進(jìn)行處理。
然而,Unicode 中,emoji 表情是4個(gè)字節(jié),例如表情符號:
??,它對應(yīng)的 Bytes 型數(shù)據(jù)為:
\xf0\x9f\xa4\x94,如下圖所示:
如果我把青??南轉(zhuǎn)換為 Bytes 型數(shù)據(jù),值為:
\xe9\x9d\x92\xf0\x9f\xa4\x94\xe5\x8d\x97,如下圖所示,一共10個(gè) Bytes 字符:
那么問題來了,當(dāng)我對這個(gè) Bytes 型數(shù)據(jù)進(jìn)行 decode 會(huì)怎么樣呢?如下圖所示:
Python 可以正確地把 Bytes 數(shù)據(jù)劃分為:
- \xe9\x9d\x92 對應(yīng)“青”
- \xf0\x9f\xa4\x94 對應(yīng)“🤔”
- \xe5\x8d\x97 對應(yīng)“南”
為什么 Python 知道要把\xf0\x9f\xa4\x94這4個(gè)符號分到一組?為什么不會(huì)像下面這樣分組?
- \xe9\x9d\x92
- \xf0\x9f\xa4
- \x94\xe5\x8d\x97
實(shí)際上,這個(gè)問題的原因,只有當(dāng)我們用二進(jìn)制來看的時(shí)候,才能發(fā)現(xiàn)端倪。 青對應(yīng)的第一個(gè) Bytes 字符\xe9,其中的e9是一個(gè)十六進(jìn)制數(shù)字,把它轉(zhuǎn)成十進(jìn)制是233,轉(zhuǎn)成二進(jìn)制是11101001。 南對應(yīng)的第一個(gè) Bytes 字符\xe5,其中的e5是一個(gè)十六進(jìn)制數(shù)字,把它轉(zhuǎn)成十進(jìn)制是229,轉(zhuǎn)成二進(jìn)制是11100101。 ??對應(yīng)的第一個(gè) Bytes 字符\xf0,其中的f0是一個(gè)十六進(jìn)制數(shù)字,把它轉(zhuǎn)成十進(jìn)制是240,轉(zhuǎn)成二進(jìn)制是11110000。如果還看不出他們的差異,那我們把他們放在一起對比一下:
- 11101001
- 11100101
- 11110000
看出差異了嗎?中文漢字是三個(gè)字節(jié),轉(zhuǎn)換為 Bytes 型數(shù)據(jù)以后,第一個(gè)字符對應(yīng)的二進(jìn)制數(shù)是1110開頭。emoji 是4個(gè)字節(jié),轉(zhuǎn)換為 Bytes 型數(shù)據(jù)以后,第一個(gè)字符對應(yīng)的二進(jìn)制數(shù)是1111開頭。所以,當(dāng)給定一個(gè) Bytes 型數(shù)據(jù)需要給 Python 來轉(zhuǎn)換為字符串的時(shí)候,Python 是這樣判斷應(yīng)該有幾個(gè)字符一組的。
- 給定 Bytes 型數(shù)據(jù):\xe9\x9d\x92\xf0\x9f\xa4\x94\xe5\x8d\x97
- 看第一個(gè)字符對應(yīng)的二進(jìn)制數(shù)的高4位是1110,所以當(dāng)前字符和它后面兩個(gè)字符(合計(jì)3個(gè)字符)一組,進(jìn)行解析,得到青字。
- 跳過已經(jīng)解析的字符,直接來到第四位\xf0,發(fā)現(xiàn)它對應(yīng)的二進(jìn)制數(shù)高4位是1111,所以這個(gè)字符和接下來3個(gè)字符(合計(jì)4個(gè)字符)一組,解析出??。
- 跳過已經(jīng)解析的字符,來到第8位\xe5,對應(yīng)的二進(jìn)制高4位是1110,因此這個(gè)字符和接下來的兩個(gè)字符一組進(jìn)行解析,得到南。
- 完成。對于數(shù)字和英文字母,在 Unicode 里面只使用一個(gè)字節(jié)來表示,他們的 Ascii 碼小于128。而多字節(jié)的 Unicode 字符,都是從129開頭,所以英文字母數(shù)字與中文混合生成的 Bytes 型數(shù)據(jù),在解碼時(shí)也不會(huì)出現(xiàn)分組不明確的問題。
本文轉(zhuǎn)載自微信公眾號「未聞Code」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系未聞Code公眾號。