Java-ja温泉でちょうどエンコーディングの話で盛り上がっていたので@whosaysniに聞いてみた。print文でunicodeオブジェクトを出力しようとすると、出力先のシェルのエンコーディングでバイト列に変換してから出力される。
tmp$ cat > tmp.py print u'\u307b\u3052' tmp$ python tmp.py ほげ
しかし、シェルの側でリダイレクトをしていると、シェルと違ってファイルのエンコーディングは簡単に取得できないので「エンコーディングわかんないから何で出したらいいかわかんないや。中身がasciiだったらそのまま出してもいいか」とasciiに変換しようとして失敗する。
tmp$ python tmp.py > tmp.txt
Traceback (most recent call last):
File "tmp.py", line 1, in <module>
print u'\u307b\u3052'
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)で、その解決方法だけど、そのスクリプトからリダイレクトして書き出すファイルのエンコーディングがutf-8であると仮定できるなら:
import sys, codecs sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
これでOK。試してみよう。
tmp$ cat > tmp.py
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
print u'\u307b\u3052'
tmp$ python tmp.py > tmp.txt
tmp$ cat tmp.txt
ほげめでたしめでたし。あと@tokibitoに chardet ってライブラリでエンコーディングの判定ができると教えてもらった。今回のケースには使わないけど必要になることもありそうだから覚えておこう。