読者です 読者をやめる 読者になる 読者になる

コマンドプロンプトでのPythonの文字コードについて

Pythonでコマンドツールを作ってて、パイプでつなげたりするときに色々はまったのでメモ。
WindowsXPでPython2.7。

# coding: utf-8


def main():
    print u"テスト"  # PYTHONIOENCODINGでエンコードされる
    print "テスト"  # このファイルの文字コード(utf-8)
    print u"テスト".encode('cp932')  # cp932にエンコードされる

if __name__ == '__main__':
    main()

これをコマンドプロンプトから実行するとこうなる。

>python test.py
テスト
繝・せ繝
テスト

この出力を、文字化けがない状態でファイルに書き出したい。
ちなみに、出力をリダイレクトしようとするとエラーになる。

>python test.py > test.txt
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    main()
  File "test.py", line 5, in main
    print u"繝・せ繝・  # PYTHONIOENCODING縺ァ繧ィ繝ウ繧ウ繝シ繝峨&繧後k
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

PYTHONIOENCODINGにcp932を設定すると通る
1. Command line and environment — Python 3.5.1 documentation

>set PYTHONIOENCODING=cp932
>python test.py > test.txt
>type test.txt
テスト
繝・せ繝・
テスト

さてここで、2行目のUTF-8の化けてるものを読める状態にしたい。
chardet モジュールを使ってこんなスクリプトを書いた。

encfix-script.py

#!C:/Python27/python.exe
import sys
import chardet


def main():
    s = sys.stdin.read()
    for line in s.splitlines():
        result = chardet.detect(line)
        sys.stdout.write(line.decode(result['encoding']).encode('cp932') + '\n')

if __name__ == '__main__':
    main()

あとは、setuptoolsのexe(easy_install.exeとか)を"encfix.exe"と言う名前でコピーしてパスの通った同じディレクトリに置く。

>python test.py|encfix > test.txt
>type test.txt
テスト
テスト
テスト

期待通りの結果を得られた。

2011/11/21追記

標準エラー出力を変換したい場合は、リダイレクトすれば良い。

>python test.py 2>&1|encfix