web-dev-qa-db-ja.com

Pythonで標準出力をパイピングするときに正しいエンコーディングを設定する

Pythonプログラムの出力をパイプ処理するとき、Pythonインタプリタはエンコーディングについて混乱し、それをNoneに設定します。これはこのようなプログラムを意味します。

# -*- coding: utf-8 -*-
print u"åäö"

通常の実行時にはうまく動作しますが、失敗した場合は:

UnicodeEncodeError: 'ascii'コーデックは位置uの文字u '\ xa0'をエンコードできません:序数が範囲外(128)

パイプシーケンスで使用した場合.

配管時にこれを機能させるための最良の方法は何ですか? Shell/filesystem /を使用しているものなら何でもエンコードするように言うことができますか?

これまで見てきた提案は、あなたのsite.pyを直接修正するか、このハックを使ってデフォルトのコーディングをハードコーディングすることです:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"

配管作業をするより良い方法はありますか?

314
Joakim Lundborg

Pythonは、端末アプリケーションが使用しているエンコーディングに合わせて出力をエンコードするため、スクリプトで実行してもコードは機能します。あなたが配管しているなら、あなたはそれを自分でエンコードしなければなりません。

経験則は、次のとおりです。常に内部でUnicodeを使用します。受け取ったものをデコードし、送信したものをエンコードします。

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

もう1つの教訓的な例は、ISO-8859-1とUTF-8の間で変換し、その間のすべてを大文字にするPythonプログラムです。

import sys
for line in sys.stdin:
    # Decode what you receive:
    line = line.decode('iso8859-1')

    # Work with Unicode internally:
    line = line.upper()

    # Encode what you send:
    line = line.encode('utf-8')
    sys.stdout.write(line)

システムのデフォルトエンコーディングを設定することは悪い考えです。なぜならあなたが使うモジュールやライブラリの中にはそれがASCIIであるという事実に頼るものがあるからです。しないでください。

151
nosklo

まず、この解決策に関して:

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

毎回与えられたエンコーディングで明示的に印刷することは実用的ではありません。それは反復的でエラーが発生しやすいでしょう。

より良い解決策は、プログラムの開始時にsys.stdoutを変更して、選択したエンコードでエンコードすることです。これが私が見つけた1つの解決策です Python:sys.stdout.encodingはどのように選ばれますか? 、特に "toka"によるコメント:

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
166
Craig McQueen

環境変数 "PYTHONIOENCODING"を "utf_8"に変更してみてください。私は この問題に関する私の試練のページ を書きました。

Tl;ブログ投稿のdr:

import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))

あなたにあげる

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻
120
daveagp
export PYTHONIOENCODING=utf-8

仕事はしますが、Python自体には設定できません...

できることは、設定されていないかどうかを確認してから、scriptを呼び出す前に設定するようにユーザーに指示することです。

if __== '__main__':
    if (sys.stdout.encoding is None):
        print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
        exit(1)

コメントに返信するように更新しました。問題は標準出力へのパイプ処理時にのみ発生します。私はFedora 25 Python 2.7.13でテストしました

python --version
Python 2.7.13

猫b.py

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

print sys.stdout.encoding

./b.pyを実行中

UTF-8

を実行しています。もっと少なく

None
60
Sérgio

私は 先週の同様の問題 がありました。私のIDE(PyCharm)で修正するのは簡単でした。

これが私の修正でした:

PyCharmのメニューバーから:File - > Settings ... - > Editor - > File Encodingsを設定し、それから "IDE Encoding"、 "Project Encoding"、 "Properties encodingのデフォルトエンコーディング"をすべてUTF-8に変更し、動作します。魔法のように。

お役に立てれば!

5
CLaFarge

クレイグマックイーンの答えの懐疑的なサニタイズ版。

import sys, codecs
class EncodedOut:
    def __init__(self, enc):
        self.enc = enc
        self.stdout = sys.stdout
    def __enter__(self):
        if sys.stdout.encoding is None:
            w = codecs.getwriter(self.enc)
            sys.stdout = w(sys.stdout)
    def __exit__(self, exc_ty, exc_val, tb):
        sys.stdout = self.stdout

使用法:

with EncodedOut('utf-8'):
    print u'ÅÄÖåäö'
4
Tompa

次の呼び出しでそれを "自動化"することができます。

def __fix_io_encoding(last_resort_default='UTF-8'):
  import sys
  if [x for x in (sys.stdin,sys.stdout,sys.stderr) if x.encoding is None] :
      import os
      defEnc = None
      if defEnc is None :
        try:
          import locale
          defEnc = locale.getpreferredencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.getfilesystemencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.stdin.encoding
        except: pass
      if defEnc is None :
        defEnc = last_resort_default
      os.environ['PYTHONIOENCODING'] = os.environ.get("PYTHONIOENCODING",defEnc)
      os.execvpe(sys.argv[0],sys.argv,os.environ)
__fix_io_encoding() ; del __fix_io_encoding

はい、この "setenv"が失敗した場合、ここで無限ループに陥る可能性があります。

2
jno

私はレガシアプリケーションでこの問題に遭遇しました、そして、何が印刷されたかを特定することは困難でした。私はこのハックを手助けしました:

# encoding_utf8.py
import codecs
import builtins


def print_utf8(text, **kwargs):
    print(str(text).encode('utf-8'), **kwargs)


def print_utf8(fn):
    def print_fn(*args, **kwargs):
        return fn(str(*args).encode('utf-8'), **kwargs)
    return print_fn


builtins.print = print_utf8(print)

私のスクリプトの上に、test.py:

import encoding_utf8
string = 'Axwell Λ Ingrosso'
print(string)

これは全てのprintへの呼び出しをエンコーディングを使うように変更するので、あなたのコンソールはこれを出力するでしょう:

$ python test.py
b'Axwell \xce\x9b Ingrosso'
1
cessor

何が起こっているのか最終的に気付く前に、ここで私は長い間実験に費やさなければならなかった何かをここで言及したいと思いました。これはここにいるすべての人にとって非常に明白なので、彼らはそれに言及することに煩わされていません。しかし、それがあれば私の助けになっていたでしょう。その原則に基づいて...!

NB:私は Jython 特にv 2.7を使っています、だからおそらくこれは CPython には当てはまらないかもしれません...

NB2:ここに私の.pyファイルの最初の2行は次のとおりです。

# -*- coding: utf-8 -*-
from __future__ import print_function

"%"(AKA "補間演算子")の文字列構築メカニズムも追加の問題を引き起こします。 "environment"のデフォルトのエンコーディングがASCIIで、次のようにした場合

print( "bonjour, %s" % "fréd" )  # Call this "print A"

あなたはEclipseで実行するのに問題はないでしょう... Windows CLI(DOSウィンドウ)では、エンコーディングは コード・ページ85 (私のWindows 7 OS)かそれに似たもので、ヨーロッパを処理することができます。少なくともアクセント付きの文字なので、うまくいきます。

print( u"bonjour, %s" % "fréd" ) # Call this "print B"

また動作します。

もしあなたがCLIからファイルにアクセスするなら、stdoutエンコーディングはNoneになるでしょう、それはデフォルトでASCII(私のOSでは)になります、そしてそれは上記のプリントのどちらも扱うことができないでしょう...(恐ろしいエンコードエラー).

それで、あなたはを使ってあなたの標準出力をリダイレクトすることを考えるかもしれません

sys.stdout = codecs.getwriter('utf8')(sys.stdout)

そして、CLIでファイルに接続して実行してみてください。非常に奇妙なことに、上のprint Aは動作します...しかし上のprint Bはエンコーディングエラーを発生させます!しかし、以下はうまくいきます。

print( u"bonjour, " + "fréd" ) # Call this "print C"

私が(暫定的に)持ってきた結論は、接頭辞 "u"を使用して nicode 文字列として指定された文字列が%処理メカニズムに送信された場合です。 stdoutをredirectに設定しているかどうかにかかわらず、デフォルトの環境エンコーディングを使用します。

人々がこれにどう対処するかは選択の問題です。私はUnicodeの専門家に、なぜこれが起こるのか、どういうわけか間違っているのか、これにどのような好ましい解決策が当てはまるのか、---にも当てはまるのかどうか CPython 、Python 3で起こるのかどうかを述べたいなどなど.

1
mike rodent

Ubuntu 12.10およびGNOME Terminalでは、プログラムが標準出力に印刷しているとき、または他のプログラム用のパイプに接続されているときにエラーは発生しません。ファイルエンコーディングと端末エンコーディングの両方が TF-8 です。

$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö

どのOSとターミナルエミュレータを使用していますか? iTerm 2 とOS Xを使用した場合、私の同僚の中には同様の問題を抱えていると聞きました。 iTerm 2が原因かもしれません。

更新:この答えは間違っています - 詳細についてはコメントを参照してください

1
Fish Monitor