6

Pythonでsysadminスクリプトを書いているとき、print()のすべての呼び出しに影響を与えるsys.stdoutのバッファーは、バッファーがフラッシュされるのを待ってから大量の行を取得したくないため、煩わしいものです。画面に表示されたら、代わりに、スクリプトによって新しい出力が生成されるとすぐに、出力の行を個別に取得したいと思います。改行を待ちたくないので、出力を見てください。

Pythonでこれを行うためによく使用されるイディオムは

import os
import sys
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)

これは私にとって長い間うまくいきました。ここで、Unicodeでは機能しないことに気づきました。次のスクリプトを参照してください。

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import print_function, unicode_literals

import os
import sys

print('Original encoding: {}'.format(sys.stdout.encoding))
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)
print('New encoding: {}'.format(sys.stdout.encoding))

text = b'Eisb\xe4r'
print(type(text))
print(text)

text = text.decode('latin-1')
print(type(text))
print(text)

これにより、次の出力が生成されます。

Original encoding: UTF-8
New encoding: None
<type 'str'>
Eisb▒r
<type 'unicode'>
Traceback (most recent call last):
  File "./export_debug.py", line 18, in <module>
    print(text)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 4: ordinal not in range(128)

その理由を突き止めるのに何時間もかかりました(私の元のスクリプトは、この最小限のデバッグスクリプトよりもはるかに長かったです)。ラインです

sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)

私は何年も使っていたので、問題はないと思いました。この行をコメントアウトすると、正しい出力は次のようになります。

Original encoding: UTF-8
New encoding: UTF-8
<type 'str'>
Eisb▒r
<type 'unicode'>
Eisbär

では、スクリプトメントは何をするのでしょうか?Python2.7コードをPython3.xにできるだけ近づけるために、私は常に使用しています

from __future__ import print_function, unicode_literals

これにより、Pythonは新しいprint()関数を使用しますが、より重要です。これにより、Pythonはデフォルトですべての文字列を内部でUnicodeとして保存します。たとえば、Latin-1/ISO-8859-1でエンコードされたデータがたくさんあります。

text = b'Eisb\xe4r'

意図した方法で使用するには、最初にUnicodeにデコードする必要があります。

text = text.decode('latin-1')

です。私のシステムではデフォルトのエンコードはUTF-8であるため、文字列を出力するたびに、Pythonは内部Unicode文字列をUTF-8にエンコードします。ただし、最初に、内部的に完全なUnicodeである必要があります。

これで、これまでのところゼロバイトの出力バッファではなく、すべてが一般的に正常に機能するようになりました。何か案は?ゼロバッファリング行の後でsys.stdout.encodingが設定されていないことに気付きましたが、再度設定する方法がわかりません。これは読み取り専用の属性であり、OS環境変数LC_ALLまたはLC_CTYPEは、Pythonインタープリターの開始時にのみ評価されるようです。

ところで:「Eisbär」はドイツ語で「ホッキョクグマ」を意味します。

4

2 に答える 2

6

print 関数は、ファイル オブジェクトへの書き込み時に特別なフラグを使用します。これPyFile_WriteObjectにより、Python C API の関数が出力エンコーディングを取得して Unicode からバイトへの変換を実行し、stdoutストリームを置き換えることでエンコーディングが失われます。残念ながら、明示的に再度設定することはできません。

encoding = sys.stdout.encoding
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)
sys.stdout.encoding = encoding  # Raises a TypeError; readonly attribute

必要なオプションを使用できるようにしたい場合は、バッファリングを無効にすることができないため、代わりにio.open関数を使用することもできません。encoding

print 関数をすぐにフラッシュする適切な方法は、次のflush=Trueキーワードを使用することです。

print(something, flush=True)

どこにでも追加するのが面倒な場合は、カスタム印刷関数の使用を検討してください。

def print(*args, **kw):
    flush = kw.pop('flush', True)  # Python 2.7 doesn't support the flush keyword..   
    __builtins__.print(*args, **kw)
    if flush:
        sys.stdout.flush()

Python 2.7 のprint()関数は実際にはまだ flush キーワードをサポートしていないため (面倒)、そのカスタム バージョンで代わりに明示的なフラッシュを追加することでシミュレートできます。

于 2012-10-10T20:02:12.843 に答える
0

の引数のはb、ファイルがバイナリ モードで開かれている必要があることを示しています。これが、Unicode が機能しない理由です。さらに、Python 3 では、通常の文字列をそのように構成された標準出力に出力できません。それは言います。'wb'sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)TypeError: a bytes-like object is required, not 'str'

上記の「sysadmin スクリプト」の使用例では、行バッファリングprint("mytext")を使用するだけで十分です。つまり、すべての通常のステートメントの最後など、改行が書き込まれるたびに出力がフラッシュされます。行バッファリングの場合は、次のように書くだけで十分です:

import os
import sys

sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)  # 1 : line buffered

./myprogram.py | catこれは、標準出力が別のプログラムによって読み取られる可能性のあるパイプ (最も単純なケース: ) にリダイレクトされる場合に、行ごとの出力を得るために必要であることがわかりました。

行の一部をすぐにフラッシュする必要がある場合は、次を使用できます。

print("mytext", end="", flush=True)
于 2021-01-14T14:55:48.673 に答える