2008.09.09 Tuesday
: 'ascii' codec can't encode characters in position 6-32: ordinal not in range(128)
このエラーメッセージをみて、ハッと心を奪われた同士諸君、こんばんわ。そして、
: coercing to Unicode: need string or buffer, NoneType found
このエラーメッセージを見るたびに悪夢のリフレインに苦しむ戦士のみなさま、ようこそおいでくださった。
見果てぬ夢を胸に抱きながら、google app engineの独自仕様(と、Pythonの仕様でもあるのですが・・・)に打ち砕かれた日々も今日で終わる。
今から、インドの奥地でわしが取得した奥義を伝授しよう!
と、大げさに始めてみましたが、どうもここで苦しむ方が多い気がするので、わたしが四苦八苦してなんとか回避できるようになった方法をここに書いておきます。
まずは、実際に、google app engineで動作することが確認されているコードから。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import cgi
import codecs
f = cgi.FieldStorage()
if f.getvalue('text','') == "":
html_body = u"""
ここにフォーム側htmlを記載
"""
print "Content-type: text/html¥n"
print html_body.encode('utf-8')
else:
html_body = u"""
ここに、表示側htmlを記載"""
text = f.getvalue('text','')
text = unicode(text,'utf-8')
text = text.replace("######","your name")
print "Content-type: text/html¥n"
print (html_body % text).encode('utf-8')
(色分けには、こちらのスクリプトを使用しました)
■pyhighlight.py:Python ソースを HTML で色分け
http://tuchinoko.moe-nifty.com/oboegaki/2005/05/pyhighlightpypy_932e.html
Pythonのユニコード問題はこちら。
■PythonのUnicodeEncodeErrorを知る
http://lab.hde.co.jp/2008/08/pythonunicodeencodeerror.html
■コードの全文はこちら
html://story-fact.com/test10.txt
■実際に動いているところはこちら
http://story-fact.appspot.com/
google app engineのためにPythonを覚え始めたわたしような方は、ついphpのような言語と同じ感覚でPythonのコードを書いてしまいがちです。
わたしがはまったのは、phpのechoと同じ感覚でPythonのprintを使ってしまっていたところ。phpのコードでは、htmlに文字列を埋め込む時にechoをよく使いますが、Pythonでは、これは通用しないと思った方がよいかもです。
print (html_body % text).encode('utf-8')
のように、埋め込んだ方がよいと思われます。
これは、「PythonのUnicodeEncodeErrorを知る」に詳しいのですが、PythonのUnicodeの取り扱いが結構特殊だからです。また、Pythonはあまりマルチバイトの処理が得意ではないからです。phpのような感覚でいるとはまるかと思います。
知っていた方が良さそうなのは、
1.PythonのUnicodeは、いわゆるUTF-8ではない。
純粋なUnicodeの方です。
UTF-8の方は8バイト文字と呼ばれ、別の種類の文字列として区別されています。
2.Pythonの内部では、マルチバイト文字列はUnicodeでないと処理できない。
これも、知らないと結構はまるのですが(当然できるものと思い込んでいるため)、たとえば「+」を使って文字列を連結することもできません。ですので、フォームなどでマルチバイト文字列を受けとって加工するときは(置換など)、Unicodeに変換してやらなければなりません。
3.Unicodeに変換するとき、値が空だとエラーになる。
これも、嘘だろwww と思ったのですが、例外処理してやらなければなりません。
f = cgi.FieldStorage()
if f.getvalue('text','') != "":
みたいな。
4.google app engine ではUnicodeはprintしてはならない。
これは、Pythonのprintが暗黙のうちに文字コードを変換するという仕様に絡むのですが、google app engineでは、必ずエラーとなります。
# -*- coding: utf-8 -*-
のように文字コードを指定しても、google app engineではこのUnicode文字列をprintすると、asciiに変換しようとするからです。
ですので、鉄則として、
・空を例外処理してマルチバイト文字列をUnicodeに変換
↓
・Unicode文字列で内部処理
↓
・マルチバイト文字列にエンコードして、print
という流れになります。
以上、簡単ですが、勘所を書いておきました。
少しでも助けになれば幸いです。
わたしも何度も挫折しそうになりましたが(^_^; これを機にすてきなアプリケーションを作ってくださいね!
PS.
これ、一つ一つ理解するのに、それぞれ5時間ぐらいかかったよ・・・。とほほ・・・。