2008年8月12日 星期二

app engine處理中文通用解法

app engine處理中文可能會碰到一些問題 找了各種寫法之後整理成以下這樣 (注意粗體部份) 只要拿這個範例去改 應該就都不會再碰到問題了

#coding=utf-8
#編碼要改成utf8 拿資料有中文才不會錯
import sys
reload(sys)
sys.setdefaultencoding('utf8')

import wsgiref.handlers
import os
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import template
#自定義模組
import otherpage1
import otherpage2

#主頁
class MainPage(webapp.RequestHandler):
def get(self):
template_values = {#載入模版內的變數
}
path = os.path.join(os.path.dirname(__file__), 'main.html')#模版路徑
self.response.out.write(template.render(path, template_values))#寫入模版

def main():
application = webapp.WSGIApplication(
[('/', MainPage)
,('/otherpage1', otherpage1.getpage)
,('/otherpage2', otherpage2.getpage)
],
debug=True)
wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
main()

This is some sample text. You are using FCKeditor.


重點:

1. py檔頭要寫
#coding=utf-8

2. 傳中文參數要寫u
ex:
myfunction(var1,u"我是中文參數2")

3. 資料庫中有中文值要取出
ex:
self.response.out.write( str(record.tag) )
假如資料項record中欄位tag的值是中文 那這樣寫就會出錯
會出現 UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
正確寫法應該像下面這樣 在欄位後加上.encode('utf-8')
self.response.out.write( cgi.escape( str( record.tag.encode('utf-8') ) ) )
另外加上cgi.escape()可以防止文件中javascript插入執行 防惡搞




其他補充:
2008/10/16python unicode encode & url encode
2009/08/15 新心得 解決方法: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

app engine寫中文最常遇到的就是編碼錯誤
其中又以"UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)" 最常見
摸了一晚總算歸納出一個統一的解決方法

首先要瞭解unicodeutf-8是不一樣的 常會搞在一起就以為是一樣的東西 unicode指的是萬國碼 是一種"字碼表" 而utf-8是這種字碼表儲存的編碼方法 unicode不一定要由utf-8這種方式編成bytecode儲存 也可以編成utf-16,utf-7等其他方式 只是目前大家大多都以utf-8的方式編成bytecode (更詳細說明可以看一下unicode補完計劃中Unicode、UTF-8、UCS等字的意義)

知道unicodeutf-8的差別後 再來參考這篇
How to Use UTF-8 with Python
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)

所以解決方法有3種
一種是全都轉byte string
一種是全都轉unicode string
第三種是更改設定預設解碼器為utf-8

app engine上我不知道怎麼更改設定預設解碼器 所以只說前2種

1.全都轉byte string
self.response.out.write("你好"+self.request.get("argu").encode('utf-8'))

2.全都轉unicode string
self.response.out.write(u"你好"+self.request.get("argu"))

這樣就再也不會有"UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)"的問題產生了 遇到噴出這種錯誤幾乎都可以用這2種方法解決

我個人是覺得用第一種全都轉byte string比較好
因為一但用了unicode string 以後有中文字串前面都要掛個u 看起來不是很直覺 而且容易漏掉
不如在遇到函式有中文結果(或是資料庫撈出來的結果)直接都encode成utf-8就好