首頁  >  文章  >  後端開發  >  Python編碼的深入淺出分析

Python編碼的深入淺出分析

黄舟
黄舟原創
2017-07-18 13:27:421046瀏覽

據說,每個做Python 開發的都被字元編碼的問題搞暈過,最常見的錯誤就是UnicodeEncodeError、UnicodeDecodeError,你好像知道怎麼解決,遺憾的是,錯誤又出現在其它地方,問題總是重蹈覆轍,str 到unicode 之間的轉換用decode 還是encode 方法還特不好記,老是混淆,問題究竟出在哪裡?

為了弄清楚這個問題,我決定從python 字串的構成以及字元編碼的細節上進行深入淺出的分析

位元組與字元

電腦儲存的一切數據,文字字元、圖片、視訊、音訊、軟體都是由一串01的位元組序列構成的,一個位元組等於8個位元組。

而字元就是一個符號,例如一個漢字、一個英文字母、一個數字、一個標點都可以稱為一個字元。

位元組方便儲存和網路傳輸,而字元用於顯示,方便閱讀。例如字元「p」 儲存到硬碟是一串二進位資料 01110000,佔用一個位元組的長度

編碼與解碼

我們用編輯器開啟的文本,看到的一個個字符,最終保存在磁碟的時候都是以二進位位元組序列形式存起來的。那麼從字元到位元組的轉換過程就叫做編碼(encode),反過來叫做解碼(decode),兩者是一個可逆的過程。編碼是為了儲存傳輸,解碼是為了方便顯示閱讀。

例如字元 “p” 經過編碼處理儲存到硬碟是一串二進位位元組序列 01110000 ,佔用一個位元組的長度。字元 “禪” 有可能是以 “11100111 10100110 10000101″ 佔用3個位元組的長度存儲,為什麼說是有可能呢?這個放到後面再說。

Python 的編碼為什麼那麼蛋會痛?當然,這不能怪開發者。

這是因為 Python2 使用 ASCII 字元編碼作為預設編碼方式,而 ASCII 不能處理中文,那麼為什麼不使用 UTf-8 呢?因為Guido 老爹為Python 編寫第一行程式碼是在1989年的冬天,1991年2月正式開源發布了第一個版本,而Unicode 是1991年10月發布的,也就是說Python 這門語言創立的時候UTF-8 還沒誕生,這是其一。

Python 把字串的類型還搞成兩種,unicode 和 str ,以至於把開發者都弄糊塗了,這是其二。 python3 徹底把 字串重新改造了,只保留一種類型,這是後話,以後再說。

str與unicode

Python2 把字串分成 unicode 和 str 兩種。本質上 str 是一串二進位位元組序列,下面的範例程式碼可以看出 str 類型的 「禪」 列印出來是十六進位的 \xec\xf8 ,對應的二進位位元組序列就是 ’11101100 11111000′。

>>> s = '禅'
>>> s
'\xec\xf8'
>>> type(s)
<type &#39;str&#39;>

而unicode 類型的u」禪」 對應的unicode 符號是u'\u7985′

>>> u = u"禅"
>>> u
u&#39;\u7985&#39;
>>> type(u)
<type &#39;unicode&#39;>

我們要把unicode 符號儲存到檔案或傳輸到網路就需要經過編碼處理轉換成str 類型,於是python 提供了encode 方法,從unicode 轉換到str,反之亦然。

encode

>>> u = u"禅"
>>> u
u&#39;\u7985&#39;
>>> u.encode("utf-8")
&#39;\xe7\xa6\x85&#39;

decode

>>> s = "禅"
>>> s.decode("utf-8")
u&#39;\u7985&#39;
>>>

不少初學者怎麼也記不住str 與unicode 之間的轉換用encode 還是decode,如果你記住了str 本質上其實是一串二進位數據,而unicode 是字元(符號),編碼(encode)就是把字元(符號)轉換為二進位資料的過程,因此unicode 到str 的轉換要用encode 方法,反過來就是用decode 方法。

encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string”.

清楚了 str 與 unicode 之間的轉換關係之後,讓我們來看看什麼時候會出現 UnicodeEncodeError、UnicodeDecodeError 錯誤。

UnicodeEncodeError

UnicodeEncodeError 發生在unicode 字串轉換成str 位元組序列的時候,來看一個例子,把一串unicode 字串儲存到檔案

# -*- coding:utf-8 -*-
def main():
    name = u&#39;Python之禅&#39;
    f = open("output.txt", "w")
    f.write(name)

錯誤日誌

UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 6-7: ordinal not in range(128)

為什麼會出現UnicodeEncodeError?

因為呼叫write 方法時,Python 會先判斷字串是什麼類型,如果是str,就直接寫入文件,不需要編碼,因為str 類型的字串本身就是一串二進位的位元組序列了。

如果字串是unicode 類型,那麼它會先呼叫encode 方法把unicode 字串轉換成二進位形式的str 類型,才儲存到文件,而encode 方法會使用python 預設的ascii 碼來編碼

相當於:

>>> u"Python之禅".encode("ascii")

但是,我們知道ASCII 字符集中只包含了128個拉丁字母,不包括中文字符,因此出現了'ascii' codec can't encode characters 的錯誤。要正確地使用 encode ,就必須指定一個包含了中文字元的字元集,例如:UTF-8、GBK。

>>> u"Python之禅".encode("utf-8")
&#39;Python\xe4\xb9\x8b\xe7\xa6\x85&#39;

>>> u"Python之禅".encode("gbk")
&#39;Python\xd6\xae\xec\xf8&#39;

所以要把 unicode 字串正確地寫入文件,就應該預先把字串進行 UTF-8 或 GBK 編碼轉換。

def main():
    name = u&#39;Python之禅&#39;
    name = name.encode(&#39;utf-8&#39;)
    with open("output.txt", "w") as f:
        f.write(name)

当然,把 unicode 字符串正确地写入文件不止一种方式,但原理是一样的,这里不再介绍,把字符串写入数据库,传输到网络都是同样的原理

UnicodeDecodeError

UnicodeDecodeError 发生在 str 类型的字节序列解码成 unicode 类型的字符串时

>>> a = u"禅"
>>> a
u&#39;\u7985&#39;
>>> b = a.encode("utf-8")
>>> b
&#39;\xe7\xa6\x85&#39;
>>> b.decode("gbk")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;gbk&#39; codec can&#39;t decode byte 0x85 in position 2: incomplete multibyte sequence

把一个经过 UTF-8 编码后生成的字节序列 ‘\xe7\xa6\x85′ 再用 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: &#39;ascii&#39; codec can&#39;t decode byte 0xe4 in position 0: ordinal not in range(128)
>>>

str 与 unicode 字符串 执行 + 操作是,Python 会把 str 类型的字节序列隐式地转换成(解码)成 和 x 一样的 unicode 类型,但Python是使用默认的 ascii 编码来转换的,而 ASCII 中不包含中文,所以报错了。

>>> y.decode(&#39;ascii&#39;)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe4 in position 0: ordinal not in range(128)

正确地方式应该是显示地把 y 用 UTF-8 或者 GBK 进行解码。

>>> x = u"Python"
>>> y = "之禅"
>>> y = y.decode("utf-8")
>>> x + y
u&#39;Python\u4e4b\u7985&#39;

以上内容都是基于 Python2 来讲的,关于 Python3 的字符和编码将会另开一篇文章来写,保持关注。

以上是Python編碼的深入淺出分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn