Pythonの鬼門として文字コードの扱いは必ず挙げられると思います。
Pythonを使い始めた初期はこの文字コードの扱いに辟易して二度と使うか!と思ってた時期もありました。
今はもう慣れましたが。。。
さて何をしようとしていたか
- Pythonスクリプトで、DBのAPIでクエリログを取得する。
- ファイルに書き込む。
もうこんだけです。
そのクエリログに日本語コメントが入っていると、書き込んだ時に冒頭のUnicodeEncodeErrorが起きることがありました。
その状況と解決を書きてみます。
Python は2.7です。今時ごめんなさい。。。。
確認したときは大丈夫だったのに...
DB APIで取得したときは問題なくクエリログを取得できて不要な文字列なども削除できていました。
お試しのログを書き込むこともできた!
そこで、「あーじゃあもうファイルに書き込むだけじゃん!」っと思ってたんですね。
ではそのようにをファイルに書き込んでみましょう。
# -*- coding: utf-8 -*-
# 実際はAPIでログを取得しますが、ここでは動作確認のために文字列のログにします。
log = "aaa 日本語"
with open("test.txt", "a") as f:
f.write(log + "\r\n")
ファイルのtest.txtをみると
aaa 日本語
ちゃんと書かれてる。じゃあもうAPIからクエリログを取得して動かすだけだ。
っと思っていたんですね。そこでAPIからログを取得して書き込んでみたら下記のようなエラーが。。
(すみませんがAPIの部分は省略させてもらいます)
Traceback (most recent call last):
File "writetest.py", line 11, in <module>
f.write(log + "\r\n")
UnicodeEncodeError: 'ascii' codec can't encode characters in position 4-6: ordinal not in range(128)
結論から言うと、APIで取得した文字列がUnicode型で扱われていました。
どうもpython2ではwriteで書き込むときはデフォルトのasciiで書き込もうとするから起きるエラーのようです。
あまり詳しく調べられてませんが、そういうものみたいです。
どうやって解決したかというと
log = log.encode("utf_8")
というように一度utf-8に変換して書き込みました。
# -*- coding: utf-8 -*-
# APIでログを取得
log = (APIでログを取得)
log = log.encode("utf_8")
with open("test.txt", "a") as f:
f.write(log + "\r\n")
ちなみにですが、
f.write(log.encode("utf_8") + "\r\n")
でも同様のエラーでした。
その他解決方法
上記の解決も含て
- 一度utf-8に変換する ex. log = log.encode("utf_8")
- codecモジュールを使ってファイルオープンするときに"utf_8"で開いて書き込む
- python3だとopenの時に文字コードを指定できるので、"utf_8"で開いて書き込む
といった方法があるようです。
Unicode型の動作
ちなみにUnicode型の簡単な動作を確認してみましょう。
# coding: utf-8
str_1 = "日本語"
str_2 = u"日本語"
print str_1
print str_2
print type(str_1)
print type(str_2)
print len(str_1)
print len(str_2)
print ("本" in str_1)
print (u"本" in str_2)
print str_1.find("本")
print str_2.find(u"本")
これを実行すると
日本語 # print str_1
日本語 # print str_2
<type 'str'> # print type(str_1)
<type 'unicode'> # print type(str_2)
9 # print len(str_1)
3 # print len(str_2)
True # print ("本" in str_1)
True # print (u"本" in str_2)
3 # print str_1.find("本")
1 # print str_2.find(u"本")
ざっと見た感じ、
str型は文字列がバイト形式で扱われていて
unicode型は(人が直感的に判断できる)文字として扱われている
とわかりますね。
このことからも文字列を扱うのであればunicodeの方が扱いやすいかと思います。
そのためであれば冒頭のpythonによるAPIでログを取得したときのログはunicode型で取得されていたことが理解できます。
ちなみにですが、このへんの動きはまったく知りませんでしたw