web-dev-qa-db-ja.com

sys.stdoutエンコーディングをPython 3?

Python 2でデフォルトの出力エンコーディングを設定することはよく知られているイディオムです:

_sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
_

これは、UTF-8で出力をエンコードするコーデックライターで_sys.stdout_オブジェクトをラップします。

ただし、sys.stdout.write()にはstrが必要ですが、エンコードの結果はbytesであるため、この手法はPython 3では機能しません。 codecsがエンコードされたバイトを元の_sys.stdout_に書き込もうとするとエラーが発生します。

Python 3?でこれを行う正しい方法は何ですか?

53
Greg Hewgill

Python 3.7から reconfigure() で標準ストリームのエンコードを変更できます:

sys.stdout.reconfigure(encoding='utf-8')

errorsパラメーターを追加して、エンコードエラーの処理方法を変更することもできます。

15
sth

Python 3.1はio.TextIOBase.detach()を追加しました。 _sys.stdout_ のドキュメントに注記があります:

標準ストリームはデフォルトでテキストモードになっています。これらにバイナリデータを書き込んだり読み取ったりするには、基になるバイナリバッファを使用します。たとえば、stdoutにバイトを書き込むには、sys.stdout.buffer.write(b'abc')を使用します。 io.TextIOBase.detach()ストリームを使用すると、デフォルトでバイナリにすることができます。この関数は、stdinおよびstdoutをバイナリに設定します。

_def make_streams_binary():
    sys.stdin = sys.stdin.detach()
    sys.stdout = sys.stdout.detach()
_

したがって、Python 3.1以降の対応するイディオムは次のとおりです。

_sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
_
37
Greg Hewgill

同じエラーの解決策を探しているときにこのスレッドを見つけましたが、

すでに提案されているものの代替ソリューションは、PYTHONIOENCODING環境変数を設定することですbeforePython開始、私の使用のために-これは、Pythonが初期化された後にsys.stdoutを交換するよりも問題が少ないです。

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py

Pythonコードを編集する必要がないという利点があります。

28
ideasman42

他の答えはcodecsの使用を推奨しているように見えますが、openは私には役立ちます:

import sys
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)
print("日本語")
# Also works with other methods of writing to stdout:
sys.stdout.write("日本語\n")
sys.stdout.buffer.write("日本語\n".encode())

これは、PYTHONIOENCODING="ascii"で実行しても機能します。

26
Jack O'Connor

Python 2でデフォルトの出力エンコーディングを設定することはよく知られているイディオムです

ほら!それはPython 2?の有名なイディオムですか?それは私にとって危険な間違いのように見えます。

確かに、バイナリをstdoutに書き込もうとするスクリプトを台無しにします(たとえば、イメージを返すCGIスクリプトの場合に必要になります)。バイトと文字はまったく異なる動物です。 charsのみを受け取るものでバイトを受け入れるように指定されたインターフェイスを、モンキーパッチすることはお勧めできません。

一般に、CGIおよびHTTPはバイトを明示的に使用します。 sys.stdoutにのみバイトを送信する必要があります。 In Python 3は、sys.stdout.buffer.writeバイトを直接送信します。 charsetパラメータと一致するようにページコンテンツをエンコードすることは、アプリケーション内でより高いレベルで処理する必要があります(バイナリではなくテキストコンテンツを返す場合)。また、これはprintがCGIにとってもはや役に立たないことを意味します。

(混乱を増すために、wsgirefのCGIHandlerはごく最近までpy3kで壊れていたため、WSGIをCGIにそのようにデプロイすることはできませんでした。PEP3333およびPython 3.2

17
bobince

detach()を使用すると、インタープリターは、終了する直前にstdoutを閉じようとしたときに警告を出力します。

Exception ignored in: <_io.TextIOWrapper mode='w' encoding='UTF-8'>
ValueError: underlying buffer has been detached

代わりに、これは私のためにうまくいきました:

default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

(そしてもちろん、default_out代わりにstdout。)

10
ptomato

sys.stdoutは、テキストモードでPython 3.です。したがって、Unicodeを直接記述し、Python 2のイディオムは不要です。

これが失敗する場合Python 2:

>>> import sys
>>> sys.stdout.write(u"ûnicöde")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfb' in position 0: ordinal not in range(128)

ただし、Python 3:

>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7

もしあなたのPythonがあなたのstdoutsエンコーディングが実際に何であるかを知らないなら、それはおそらくPythonのビルドでの別の問題です。

8
Lennart Regebro