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」はドイツ語で「ホッキョクグマ」を意味します。