從python2,python3編碼問題引伸出的通用編碼原理解釋
今天使用python2編碼時遇到這樣一條異常UnicodeDecodeError: ‘ascii’ code can’t decode byte 0xef
發現是編碼問題,但是平常在python3中幾乎沒有遇到過,所以特意查了資料,原來python3和python2對於字符串的理解不一樣,在python3中,字符串默認unicode編碼
一.解釋python2和python3文本處理方式
在Python3當中,文本字符串類型(使用Unicode數據存儲)被命名為 str , 字節字符串類型被命名為 bytes 。一般情況下,實例化一個字符串會得到一個 str 對象 :
如果你想得到bytes,那就在文本之前加上前綴 b , 或者 encode 一下。
所以,很顯然,str 對象有一個encode方法,bytes 對象有一個decode方法。
在Python3中的 str 對象在Python2中叫做 unicode , bytes 對象在Python2中叫做 str
在python2中使用中文字符串的方式是在頁首聲明# *--coding:utf-8--*
二.常用編碼方式
順便在網上查了一下字符集和編碼方式的文檔,發現很多人都解釋的難以理解,所以這裏嘗試說清楚一下。這裏不解析字符集和編碼方式,統稱為編碼方式。
不論什麽編碼,在計算機中統一是按照二進制字節形式存儲,一個字節有8位
1.ASCII碼,總共有128個,用1個字節的低7位來表示
2.ISO-8859-1,128個字符表示明顯不夠用,所以ISO組織又制訂了新的標準來擴展ASCII碼,它們是ISO-8859-1到ISO-8859-15,其中ISO-8859-1涵蓋了大部分西歐編碼,所以用的最多。ISO-8859-1也是單字節編碼,總共表示256個字符,發現字符變成?時,一般是使用了ISO-8859-1編碼,規定不認識範圍內的使用3f表示的就是?
3.GB2312,雙字節編碼,範圍為A1-F7,其中A1-A9是符號區,包含682個符號,B0-F7是漢字區,包含6763個漢字,漢字使用兩個字節表示
4.GBK,用於擴展GB2312,編碼範圍8140~FEFE,總共有23940個碼位,使用GB2312編碼的漢字可以用GBK來解碼
5.UTF-16,所有字符都是用兩個字節,兩個字節為16bit,所以叫UTF-16,但是浪費空間.(同unicode)
6.UTF-8,每個編碼區域有不同的字碼長度,最長為三個字節,編碼規則如下:
(1)如果一個字節的第一位為0,那麽代表當前字符為單字節字符,占用一個字節的空間。0之後的所有部分(7個bit)代表在Unicode中的序號。
(2)如果一個字節以110開頭,那麽代表當前字符為雙字節字符,占用2個字節的空間。110之後的所有部分(7個bit)代表在Unicode中的序號。且第二個字節以10開頭
(3)如果一個字節以1110開頭,那麽代表當前字符為三字節字符,占用2個字節的空間。110之後的所有部分(7個bit)代表在Unicode中的序號。且第二、第三個字節以10開頭
(4)如果一個字節以10開頭,那麽代表當前字節為多字節字符的第二個字節。10之後的所有部分(6個bit)代表在Unicode中的序號
具體每個字節的特征可見下表,其中x
代表序號部分,把各個字節中的所有x
部分拼接在一起就組成了在Unicode字庫中的序號
Byte 1 | Byte 2 | Byte3 |
---|---|---|
0xxx xxxx | ||
110x xxxx | 10xx xxxx | |
1110 xxxx | 10xx xxxx | 10xx xxxx |
我們分別看三個從一個字節到三個字節的UTF-8編碼例子:
實際字符 | 在Unicode字庫序號的十六進制 | 在Unicode字庫序號的二進制 | UTF-8編碼後的二進制 | UTF-8編碼後的十六進制 |
$ | 0024 | 010 0100 | 0010 0100 | 24 |
¢ | 00A2 | 000 1010 0010 | 1100 0010 1010 0010 | C2 A2 |
€ | 20AC | 0010 0000 1010 1100 | 1110 0010 1000 0010 1010 1100 | E2 82 AC |
細心的讀者不難從以上的簡單介紹中得出以下規律:
- 3個字節的UTF-8十六進制編碼一定是以
E
開頭的 - 2個字節的UTF-8十六進制編碼一定是以
C
或D
開頭的 - 1個字節的UTF-8十六進制編碼一定是以比
8
小的數字開頭的
三.常見問題處理之Emoji
所謂Emoji就是一種在Unicode位於\u1F601
–\u1F64F
區段的字符。這個顯然超過了目前常用的UTF-8字符集的編碼範圍\u0000
–\uFFFF
。Emoji表情隨著IOS的普及和微信的支持越來越常見。下面就是幾個常見的Emoji:
那麽Emoji字符表情會對我們平時的開發運維帶來什麽影響呢?最常見的問題就在於將他存入MySQL數據庫的時候。一般來說MySQL數據庫的默認字符集都會配置成UTF-8(三字節),而utf8mb4在5.5以後才被支持,也很少會有DBA主動將系統默認字符集改成utf8mb4。那麽問題就來了,當我們把一個需要4字節UTF-8編碼才能表示的字符存入數據庫的時候就會報錯:ERROR 1366: Incorrect string value: ‘\xF0\x9D\x8C\x86‘ for column
。 如果認真閱讀了上面的解釋,那麽這個報錯也就不難看懂了。我們試圖將一串Bytes插入到一列中,而這串Bytes的第一個字節是\xF0
意味著這是一個四字節的UTF-8編碼。但是當MySQL表和列字符集配置為UTF-8的時候是無法存儲這樣的字符的,所以報了錯。
【附】手持兩把錕斤拷, 口中疾呼燙燙燙。 腳踏千朵屯屯屯, 笑看萬物鍩鍩鍩。
從python2,python3編碼問題引伸出的通用編碼原理解釋