由__future__中unicode_literals引起的錯誤來研究python中的編碼問題

在py2.7的項目中用了__future__模塊中的 unicode_literals 來爲兼容py3.x做準備,今天遇到一個UnicodeEncodeError的錯誤,跟了下,發現這個小坑值得注意。是怎麼樣的一個坑呢?跟着代碼看看。順便深究一下原理。

1. 未引入unicode_literals版本

#coding:utf-8
from datetime import datetime

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

這段代碼可以正常執行輸出: 03月12日 21:53

2. 引入unicode_literals

#coding:utf-8
from __future__ import unicode_literals
from datetime import datetime

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

拋出如下錯誤:

Traceback (most recent call last):
File "unicode_error_demo2.py", line 7, in <module>
      print now.strftime('%m月%d日 %H:%M')
UnicodeEncodeError: 'ascii' codec can't encode character u'\u6708' in position 2: ordinal not in range(128)

3. 解決方案一:設置運行時編碼爲utf-8

#coding:utf-8
from __future__ import unicode_literals
import sys
from datetime import datetime

reload(sys)
sys.setdefaultencoding('utf-8')

now = datetime.now()
print now.strftime('%m月%d日 %H:%M')

正常執行

4. 解決方案二: 使用byte string

#coding:utf-8
from __future__ import unicode_literals
from datetime import datetime

now = datetime.now()
print now.strftime(b'%m月%d日 %H:%M')  # 指明爲bytearray字符串

# 或者這樣也行
t = bytearray('%m月 %d %H:%M', 'utf-8')
print now.strftime(str(t))

5. 總結

這裏主要涉及到python中的編碼問題,也是很多人在剛接觸Python時感到頭疼的問題。更多基礎的東西,可以到下面的參考鏈接裏看,這裏就分析下我的這幾段代碼。

先來看 第一段代碼 ,第一段能成功執行是正常的,因爲datetime的strftime函數,接受的參數就是string(注意:string表示字節,unicode表示字符串,見參考1),因此是正常的,strftime接收到string,然後格式化最後返回。

第二段例子 我們引入了來自__future__的unicode_literals,這個模塊的作用就是把你當前模塊所以的字符串(string literals)轉爲unicode。基於這個認識來看代碼,雖然我們給 now.strftime 傳遞的還是一樣的參數,但本質已經不同——一個是string(字節)一個是unicode(字符)。而 strftime 能夠接收的參數應該是string類型的,那咱們傳了一個unicode進去,它必然要轉換一下,這一轉換就出錯了——UnicodeEncodeError。

這個地方應該詳細說下,咱們給定了一個unicode字符"月",要被轉爲string,怎麼轉呢?這時就得想到ASCII了,這是Python2.7運行時默認的編碼環境。所謂"編碼"就是用來編碼的嘛,於是python就通過ASCII來把unicode轉爲string,遂,拋錯了。

錯誤的原因在Traceback中詳細指明瞭——咱們傳進去的u'u6708' (也就是"月"字)ascii解釋不了。這個符號不在ascii的128個字符表當中,因此就拋錯了。關於字符編碼方面的內容可以查看參考5。

再來說 第三段代碼 ,我們重載了系統的編碼環境爲utf-8,於是上面的那個問題消失了,簡單來說就是utf-8可以表示更多的字符。

最後來看 第四段代碼 ,我們通過把字符串定義爲byte類型同樣解決了那個錯誤。原理也很簡單,就是先把unicode轉換爲bytes,然後再轉爲string。這段代碼裏提供了兩種方法,一個是在字符串前加 b 來聲明一個bytes(而不是unicode);第二個是對生成的unicode對象通過utf-8進行編碼爲bytearray,然後轉爲string。這個問題可以查看參考4和參考6。

上面都是the5fire自己根據資料總結出來的結論,如果有問題歡迎指出。

PS: 同樣的問題對於python built-in的getattr方法也適用。

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