寫在之前
字符編碼問題幾乎是會跟隨我們整個編程生涯的一大魔障,一不小心各種玄學的問題就會接踵而至,防不勝防,尤其是對初學者來說,碰到編碼問題簡直是就是加快了踏上從入門到放棄的傳送帶。
字符集問題
很多時候在使用 Python 編程的時候,如果不使用 Unicode,處理中文的時候會出現一些讓人頭大的事情,當然這個是針對 Python2 版本來說的,因為 Python3 默認使用的是 Unicode。具體如下所示:
>>> name = '李四'
>>> name
'æå'
>>> print(name)
李四
>>> len(name)
6
>>> name[:1]
'æ'
>>> print(name[:1])
通過上面的例子可以看到,我們在代碼中使用中文以后,求字符串的長度和對其進行切片操作都沒有按照我們預想的方式輸出結果,當然有懂得讀者知道這個問題用 Unicode 就可以輕松解決,但真的是輕松解決么?如果你對字符集編碼只是半瓶子醋,新出現的問題又會讓你頭大如斗。具體如下所示:
>>> name = u'李四'
>>> name
u'李四'
>>> name[:1]
u'李'
>>> print(name[:1])
李
>>> with open('./Desktop/test', 'a') as f:
... f.write(name)
...
Traceback (most recent call last):
File "<stdin>", line 2, in
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
上述的代碼出現了錯誤,報錯的原因很簡單,因為我們定義了一個 Unicode 字符串 u'李四',然后我們想把它保存到文本文件里,但是我們沒有指定文件的編碼,所以默認的是 ASCII 編碼,顯然用 Unicode 表示的漢字是無法用 ASCII 碼存儲的,所以就拋出了 UnicodeEncodeError 異常。
Python2 & Python3 的 Unicode
前面鋪墊的夠多,現在我們算是正式來看 Python 中的字符串與字符編碼之間的調用。
首先來說 Python3,Python3 里有兩種表示字符序列的類型,分別是 bytes 和 str,bytes 的實例包含 8 位值,str 的則包含 Unicode 字符。Python2 中也有兩種表示字符序列的類型,分別是 str 和 Unicode,它與 Python3 的不同是,str 的實例包含原始的 8 位值,而 Unicode 的實例包含 Unicode 字符。這是什么意思呢?也就是說 Python3 中字符串默認為 Unicode,但是如果在 Python2 中需要使用 Unicode,必須要在字符串的前面加一個 「u」前綴,形式參考上面例子中的寫法。
當然了,在 Python2 中也可以默認使用 Unicode 的字符串,執行下面的操作即可:
from __future__ import unicode_literals
Python 字符串有 encode 和 decode 方法,用這兩個可以對字符串進行編碼或解碼,我們來看一個在 Python2 下運行的例子:
>>> name = '李四'
>>> name
'æå'
>>> my_name = name.decode('utf8')
>>> my_name
u'李四'
>>> print(my_name)
李四
>>> my_name.encode('utf-8')
'æå'
既然我們知道了 encode 用於編碼,decode 用於解碼,那么對於之前我們拋出異常的那個例子我們可以手動解決,具體如下所示:
>>> with open('./Desktop/data.txt', 'a') as f:
... f.write(name.encode('utf-8'))
...
>>> with open('./Desktop/data.txt', 'r') as f:
... data = f.read()
...
>>> data.decode('utf-8')
u'李四'
上述代碼是字符串較短的情況,如果需要寫入的字符串很多,每次都要手動進行編碼將會變的非常低效,Python2 中有個「codecs」模塊可以解決這個問題:
>>> import codecs
>>> name
u'李四'
>>> with codecs.open('./Desktop/data.txt', 'w', encoding='utf-8') as f:
... f.write(name)
...
>>> with codecs.open('./Desktop/data.txt', 'r', encoding='utf-8') as f:
... data = f.read()
...
>>> data
u'李四'
而在 Python3 中內置的 open 就已經支持指定編碼風格:
>>> name = '李四'
>>> name
'李四'
>>> with open('./Desktop/data.txt', 'w', encoding='utf-8') as f:
... f.write(name)
...
把 Unicode 字符表示為二進制的數據有很多種辦法,最常見的就是 utf-8,但是這里需要我們明白的是,Unicode 是表現形式,utf-8 是存儲形式,utf-8 雖然是使用最廣泛的編碼,但也僅僅是 Unicode 的一種存儲形式罷了。
當然字符編碼的問題還有很多,我也不可能面面俱到,最后我還是希望你可以在一開始的時候就徹底的搞定字符編碼的東西,拿出一些時間來好好研究一下,不然這個東西會成為你編碼路上揮之不去的“噩夢”。