關於 Google App Engine 編碼的摸索心得

這次花了好一番工夫摸索,所以一定要把心得紀錄下來,以免以後又需要:

常常會遇到的問題是:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

起因大概是因為這樣的程式碼:

logging.info(filename)

只要 filename 含有中文字,就會噴這個錯誤

參考這篇技術分享:http://blog.wahahajk.com/2008/08/app-engine.html
這裡面最關鍵的這一段:

python中字串型態分成2種 一種是byte string 一種是unicode string
一般來說設定好#coding=utf-8後 所有帶中文的參數宣告
ex: mystr="你好"
都會被宣告成utf-8編碼的 byte string
但是在函中中所產生的字串 則是unicode string

byte string和unicode string都能表示中文 那有什麼差呢?
答案就是不能混用 不然會造成錯誤
例如以下這個case:
self.response.out.write("你好"+self.request.get("argu"))

"你好"會自動被宣告成byte string 而self.request.get()的結果是unicode string
所以python會自動嘗試把前面的"你好"這個byte string轉成unicode string
但預設的解碼器是ascii 所以當然解不出中文byte string
然後就噴出這個常見的錯誤
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

再回頭看上面的範例程式碼:

其中 filename 經過測試是 unicode(某一資料結構的資料成員)
這時候如果做這樣的動作 str(filename)
就會爆出以下錯誤:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

我想理由就是在做 str 型別轉換時,python 會自動去把 unicode 轉換成 byte string
預設的解碼器是 ascii,所以解不出中文 byte string,若你本地端有安裝 python 不妨可以做以下實驗:

s = u'我是中文'
str(s)

上述兩行程式在我的電腦會噴錯誤:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
理由同上

上例很明顯 s 是 unicode(不相信可以執行 type(s) 看看),在執行 str 轉換時無法順利轉成 byte string 所致,解法就如同上述網誌說的,先將 s 換成 utf-8 編碼即可

s 在 encode 之前型態是 unicode
在 encode 之後型態變成 str (可在 python 程式碼內通行的字串)


附帶一提:

我們常用的 format string,在牽涉到中文時也要格外注意
例如一個函式在GAE 上會 return :'%d年%d月%d日' % (year, month, day)
這時候會產生錯誤:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 4: ordinal not in range(128)

經過實驗發現不能這樣弄(因為問題似乎是在 format string 宣告時就會產生了, 宣告時就隱含字串相加的意味, byte string + unicode),盡量還是用加法把字串分段處理比較好,例如這樣:

return str(datetime.year) + u'年' + str(datetime.month) + u'月' + str(datetime.day) + u'日'

注意 jinja2 template engine 似乎在吃要填充的資料時只接受 unicode,所以輸出的字串格式一定要是 unicode


留言

熱門文章