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 件のコメント:
コメントを投稿