Python: 標準入出力およびコマンドライン引数でバイナリデータを取り扱う方法
Python2 の場合
str = bytes なので、普通に書けばバイナリデータにも対応できる。
1 2 3 4 5 6 7 8 9 10 11 | import sys sys.stdout.write( '### sys.argv\n' ) for arg in sys.argv: sys.stdout.write(arg) sys.stdout.write( '\n' ) sys.stderr.write( '### sys.stdin\n' ) for line in iter (sys.stdin.readline, ''): sys.stderr.write(line.rstrip()) sys.stderr.write( '\n' ) |
- 実行例 (?? の箇所は表示非対応)
$ echo $'abc\nあいう\n\xff\xfe' | python2 ./bin_stdin.py abc あいう $'\xff\xfe' ### sys.argv ./bin_stdin.py abc あいう ?? ### sys.stdin abc あいう ?? |
Python3 の場合
上記のコードはエンコードエラーになる。
これは、Python3 の sys.stdout.write が str = unicode を引数に取るため。
$ echo $'abc\nあいう\n\xff\xfe' | python3 ./bin_stdin.py abc あいう $'\xff\xfe' ### sys.argv ./bin_stdin.py abc あいう Traceback (most recent call last): File "./bin_stdin.py", line 5, in sys.stdout.write(arg) UnicodeEncodeError: 'utf-8' codec can't encode character '\udcff' in position 0: surrogates not allowed |
ポイント
- 1. sys.stdXXX.buffer を利用する
- 2. sys.args の要素を os.fsencode でエンコードする
- ただしOptionParserなどのパース処理はエンコード前に適用しないと正しく処理されない模様
以下のようなコードを書けば、Python2/3 両方に対応できる。 (flush は必ずしも必須ではない)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import sys import os PY3 = sys.version_info > = ( 3 , ) stdin = sys.stdin. buffer if PY3 else sys.stdin stdout = sys.stdout. buffer if PY3 else sys.stdout stderr = sys.stderr. buffer if PY3 else sys.stderr stdout.write(b '### sys.argv\n' ) for arg in sys.argv: stdout.write(os.fsencode(arg) if PY3 else arg) stdout.write(b '\n' ) stdout.flush() stderr.write(b '### sys.stdin\n' ) for line in iter (stdin.readline, b''): stderr.write(line.rstrip()) stderr.write(b '\n' ) stderr.flush() |
- 実行例
$ echo $'abc\nあいう\n\xff\xfe' | python3 ./bin_stdin.py abc あいう $'\xff\xfe' ### sys.argv ./bin_stdin.py abc あいう ?? ### sys.stdin abc あいう ?? |
0 件のコメント:
コメントを投稿