Python2編碼問題

使用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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章