備忘録 - #python3 で sys.std(in|out|err) の encoding を強制する について
備忘録 - #python3 で sys.std(in|out|err) の encoding を強制するについて
import sys for l in sys.stdin: print l
が動かない!って文句を言いたくなる気持ちは、非常によく理解できる。Unix的なというかC言語的には伝統的かつ完璧なイディオムで、これが正常に動作しないなんてどうかしてる。どんなプログラミング言語でも、Hello Worldの次ぐらいにはこんなプログラムを書いて、IOの使い方を試してみるものだ。
しかしながら、現代はもうUnicode時代なわけで、いや、少なくともPython3はUnicode時代であると定めてしまったわけで、K&R時代ならなんの問題もなかったこんな処理でもエラーになってしまう、そんな時代に我々は生きているのである。
何が問題かと言えば、もちろん、環境変数LANG
をC
に設定してから実行している点だ。Pythonさんにしてみれば、「LANGはCですよ、これからあなたにお渡しするテキストはASCII文字列ですよ」とOSさんに言われて、はい判りましたとsys.stdinを読んでみれば、「小飼弾」という毛深い文字が流れ込んできた。これにはさすがのPythonさんもエラーのひとつもはいて抗議しようと言うことになってしまうだろう。
昔風の「日本語アプリケーション」なら、LANG
がなんだろうとConfigure
のオプションで指定したエンコーディングを優先して無理矢理使ったりしただろうが、Pythonは環境変数の方を優先する。環境変数でASCIIだっって言ってるくせに、読めないテキスト渡したって知らないよっということだ。
で、対処方法ですが、ご本人も
バイト列として読もうにもMSBが立っているだけで殺されてしまうのです。
http://blog.livedoor.jp/dankogai/archives/51816624.html
書かれているとおり、バイト列として読み込めば良い。だけど、sys.stdin
というのはテキストIOのためのAPIで、必ずバイト列->Unicodeの変換処理が動いてしまう。
したがって、ロケールに関わりなく、バイト列が欲しいのであれば、バイトIOのためのAPIを使って
import sys for l in sys.stdin.buffer: sys.stdout.buffer.write(l)
とすれば良いだけの話だ。テキストIOを使ってバイト列を取得しようとするのは無茶だ。くれぐれも言っておくが、Python3では文字列とバイト列は完全に別物である。C言語などでの、文字ってのは8ビットの数値だ、みたいな感覚は完全に忘れて欲しい。
ちなみに、sys.stdout
などの、テキストファイルのエンコーディングをオープンしたあとに変更したいという話はもちろんあって、実はio
モジュールのPEP でも元々変更可能とされていたのだけど、実装の都合で後回しにされてしまったらしい。最近、またその話が出てきている ので、近いうちにエンコーディングが変更可能となるんじゃないかと予想している。