/dev/null

脳みそのL1キャッシュ

Python3でprint("\xff")が0xffを出力しない件について

はじめに

何もわからなくて、キレそう。勢いにまかせて書いたので特にまとまっていない。

print("\xff")が0xffを出力しない

なにはともあれこれを見てくれ。

$ python --version
Python 2.7.16
$ python -c 'print("\xff")' | xxd
00000000: ff0a                                  ...

これはPython2を使った場合。バイナリを見ると0xffを出力していることがわかる。(0x0aは改行文字なので気にしないで)

予想通りの出力ですね。しかし、Python3ではこうならない。

$ python --version
Python 3.7.2
$ python -c 'print("\xff")' | xxd
00000000: c3bf 0a                                  ...

どういうことなの…

c3bfってなんなの

そう言えば、Python3のstr型はUnicode文字列を格納するための型だった。まさか、\xffUnicodeのコードポイント?

>>> print("\xff" == "\u00ff")
True

なるほどなー…じゃあ、c3bfUnicode文字をUTF-8エンコードした結果?

>>> "\xff".encode(encoding="utf8")
b'\xc3\xbf'

なるほどなー…

bytes型で出力してみる

bytes型で出力すれば大丈夫だよな。

$ python -c 'print(b"\xff")' | xxd
00000000: 6227 5c78 6666 270a                      b'\xff'.

b'\xff'をそのまま出力しているだと…

とりあえず、print()のドキュメントを見てみるか。

キーワードなしの引数はすべて、 str() がするように文字列に変換され

ということで、print()にbytes型を渡してもstr()でラップされた結果が出力されるようだ。

>>> str(b"\xff")
"b'\\xff'"

どうすればいいんだ

sys.stdout.buffer.write("b\xff")を使えばよさそう。

$ python -c 'import sys; sys.stdout.buffer.write(b"\xff")' | xxd
00000000: ff                                       .

おわりに

文字コードには気をつけようね。それにしても、驚き最小の原則とはなんだったのか…

参考文献

docs.python.org

stackoverflow.com