print文以外の文字化けについて

今回はPython2.7でprint文以外で発生する日本語の文字化けについて書きたいと思います。
print文での文字化けについては以前書いたので以下の記事を参考にしてみてください。

具体的にどういう場合かというと例えばraw_input関数による表示です。

# coding: utf-8

a = raw_input(u"何か入力してください。")

このコードはEclipse+PyDevやPythonIDLEから実行すると暗黙のうちに文字コードの変換が行われ文字化けしないのですが、Windowsコマンドプロンプトから実行すると文字化けしてしまいます。引数にはUnicode文字列を使用していますがコマンドプロンプトではprint文と違って自動的に適切な文字コードへの変換が行われません。

# coding: utf-8

#print文ではUnicodeは適切なコードに自動変換されます
print u"大丈夫" # 化けない

そのため明示的にencodeする必要があります。

# coding: utf-8

#これは化けない
a = raw_input(u"何か入力してください。".encode("shift_jis"))

ただしこの書き方をするとEclipseでは逆に文字化けしてしまいます。おそらくEclipseは入出力をソースコードと同じ文字コードで行うようになっているからだと思われます。

どんな環境でも文字化けしない書き方があればそれに越したことはないのですがPython2.7ではそれは難しいみたいです。(Python3.0では問題ないようですが)
raw_input関数が出てくるたびに「.encode("shift_jis")」を書いたり消したりしてもいいのですが数が多いと大変ですしミスが起きそうです。
そこでできるだけ少ないコードの改変でどちらの環境にも対応できるような書き方を考えます。

print文やraw_input関数による入出力は標準入出力を通して行われるので、ここを通るときに指定の文字コードに変換するように設定しておけばよさそうです。

Pythonでは標準入出力はsysモジュールによって扱うことができ、入力がsys.stdin、出力がsys.stdoutなのですがこれらの実体はfileクラスのインスタンスです。(ファイルライクオブジェクト)
そのためファイルへの読み書きの場合と同様にcodecsモジュールをつかってラップしてやることによりそこを通る入出力にエンコード、デコードを行うことができます。

# coding: utf-8

import codecs
import sys

sys.stdout = codecs.getwriter("shift_jis")(sys.stdout) # 出力
sys.stdin = codecs.getreader("shift_jis")(sys.stdin) # 入力

a = raw_input(u"何か入力してください。")

print a

上のように書けばコマンドプロンプトでも文字化けすることなく日本語の入出力が可能です。Eclipseで実行する場合には

sys.stdout = codecs.getwriter("shift_jis")(sys.stdout) # 出力
sys.stdin = codecs.getreader("shift_jis")(sys.stdin) # 入力

の2行をコメントアウトすれば文字化けしなくなります。この指定はそのファイル全体で有効のため、個々の出力をエンコードする必要はありません。

そもそも複数の環境で動くように書かなければならないケースはあまりないかもしれませんができるだけ少ない改変で文字化けを防ぐ方法でした。