Python: 標準入出力およびコマンドライン引数でバイナリデータを取り扱う方法
Python2 の場合
str = bytes なので、普通に書けばバイナリデータにも対応できる。
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 は必ずしも必須ではない)
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 件のコメント:
コメントを投稿