使用Python2.x開發最令人頭疼的莫過於編碼問題了,最常見的錯誤就是 UnicodeEncodeError、UnicodeDecodeError。
接下來我們就來研究下相關內容
字節與字符
計算機存儲數據,比如視頻、文本等都是由字節構成的,一個字節等於8個比特位。
字符就是一個符號,如一個漢字、一個字母等。
字符方便閱讀,而字節可用於計算機存儲和網絡傳輸。硬盤裏的東西都是以二進制數據形式存在的,即字節。
編碼與解碼
當我們在txt文本中寫上一個個字符時,它們最終是以二進制字節序列的形式保存在磁盤上的,這種從字符到字節的轉換過程就叫做編碼(encode),反過來則是解碼(decode)。
那麼在Python2.x中的編碼問題爲何如此頭疼?
那是因爲Python2是默認使用ASCII 字符編碼的,而ASCII 編碼又不能處理中文。此外在Python誕生時還沒有utf-8編碼。
str與unicode
Python字符串分爲兩種類型 unicode 和 str ,本質上 str 是一串二進制字節序列。
下面的例子將str類型的’我’ 打印出16進制的\xce\xd2
>>> s = '我'
>>> s
'\xce\xd2'
>>> type(s)
<type 'str'>
而unicode類型的 ‘我’ 對應的unicode符號爲 u’\u6211’
>>> u=u'我'
>>> u
u'\u6211'
>>> type(u)
<type 'unicode'>
而unicode符號保存到文件或者傳輸到網絡需要經過 encode 編碼轉化成str類型。反之,從str類型經過 decode 轉化成 unicode。
encode
>>> u = u'我'
>>> u
u'\u6211'
>>> u.encode('utf-8')
'\xe6\x88\x91'
decode
def de():
s = '我'
print(s)
b = s.decode('utf-8')
print(b)
小結:str本質是一串二進制數據,而 unicode 是字符(符號),編碼(encode)就是把字符(符號)轉換爲 二進制數據的過程,因此 unicode 到 str 的轉換要用 encode 方法,反過來就是用 decode 方法。
UnicodeEncodeError
UnicodeEncodeError 發生在unicode字符串轉換成str字節序列的時候,例如把unicode字符串保存到文件
# -*- coding:utf-8 -*-
def main():
name = u'測試'
with open('out.txt','w') as f:
f.write(name)
報錯如下:
UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-1: ordinal not in range(128)
爲什麼出現這個錯誤?
因爲調用 write 方法時,Python 會先判斷字符串是什麼類型,如果是 str,就直接寫入文件,
如果字符串是 unicode 類型,那麼它會先調用 encode 方法把 unicode 字符串轉換成二進制形式的 str 類型,才保存到文件,而 encode 方法會使用 python 默認的 ascii 碼來編碼。
相當於:
>>> u"測試".encode("ascii")
但是,我們知道 ASCII 字符集中只包含了128個拉丁字母,不包括中文字符,因此 出現了 ‘ascii’ codec can’t encode characters 的錯誤。要正確地使用 encode ,就必須指定一個包含了中文字符的字符集,比如:UTF-8、GBK。
def main():
name = u'測試'
name = name.encode('utf-8')
with open('out.txt','w') as f:
f.write(name)
UnicodeDecodeError
UnicodeDecodeError 發生在 str 類型的字節序列解碼成 unicode 類型的字符串時
>>> a = u'我'
>>> a
u'\u6211'
>>> b = a.encode('utf-8')
>>> b
'\xe6\x88\x91'
>>> b.decode('gbk')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0x91 in position 2: incomplete multibyte sequence
- 把一個經過 UTF-8 編碼後生成的字節序列 ‘\xe6\x88\x91’再用 GBK 解碼轉換成 unicode 字符串時,出現 UnicodeDecodeError。
- 因爲 (對於中文字符)GBK 編碼只佔用兩個字節,而 UTF-8 佔用3個字節,用 GBK 轉換時,還多出一個字節,因此它沒法解析。避免 UnicodeDecodeError 的關鍵是保持 編碼和解碼時用的編碼類型一致。
字符 “我”,保存到文件中有可能佔3個字節,有可能佔2個字節,具體處決於 encode 的時候指定的編碼格式是什麼。
UnicodeDecodeError的又一個例子
>>> x = u'Python'
>>> y = '大神'
>>> x + y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xb4 in position 0: ordinal not in range(128)
str 與 unicode 字符串 執行 + 操作時,Python 會把 str 類型的字節序列隱式地轉換成(解碼)成 和 x 一樣的 unicode 類型,但Python是使用默認的 ascii 編碼來轉換的,而 ASCII字符集中不包含有中文,所以報錯了。
相當於執行了:
y.decode('ascii')
正確地方式應該是找到一種包含有中文字符的字符編碼,比如 UTF-8或者 GBK 顯示地把 y 進行解碼轉換成 unicode 類型.
def decodeTest():
x = u'Python'
y = '大神'
y = y.decode('utf-8')
print(x+y)
詳情參見,原文出處:http://python.jobbole.com/88264/