wake-up-neo.net

Festlegen der richtigen Codierung beim Weiterleiten von stdout in Python

Wenn die Ausgabe eines Python - Programms weitergeleitet wird, ist der Python - Interpreter hinsichtlich der Codierung verwirrt und setzt ihn auf None. Dies bedeutet ein Programm wie dieses:

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

wird gut funktionieren, wenn normal ausgeführt, aber scheitern mit:

UnicodeEncodeError: Der Codec 'ascii' kann das Zeichen u '\ xa0' an Position 0 nicht codieren: Ordnungszahl nicht im Bereich (128)

bei Verwendung in einer Rohrsequenz.

Was ist der beste Weg, um diese Funktion beim Leiten zu erreichen? Kann ich ihm einfach sagen, welche Codierung das Shell/Dateisystem/was auch immer verwendet?

Die Vorschläge, die ich bisher gesehen habe, sind, Ihre site.py direkt zu ändern oder die defaultencoding mit diesem Hack fest zu codieren:

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

Gibt es einen besseren Weg, um Rohrleitungen zum Laufen zu bringen?

321
Joakim Lundborg

Ihr Code funktioniert, wenn er in einem Skript ausgeführt wird, da Python codiert die Ausgabe in die von Ihrer Terminalanwendung verwendete Codierung. Wenn Sie Pipes ausführen, müssen Sie sie selbst codieren.

Als Faustregel gilt: Verwenden Sie Unicode immer intern. Dekodieren Sie, was Sie empfangen, und kodieren Sie, was Sie senden.

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

Ein weiteres didaktisches Beispiel ist ein Python) - Programm, das zwischen ISO-8859-1 und UTF-8 konvertiert und alles dazwischen in Großbuchstaben schreibt.

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)

Das Festlegen der Systemstandardcodierung ist eine schlechte Idee, da sich einige Module und Bibliotheken, die Sie verwenden, darauf verlassen können, dass es sich um ASCII handelt. Tu es nicht.

155
nosklo

Zunächst zu dieser Lösung:

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

Es ist nicht praktisch, jedes Mal explizit mit einer bestimmten Codierung zu drucken. Das wäre repetitiv und fehleranfällig.

Eine bessere Lösung ist, zu Beginn Ihres Programms sys.stdout zu ändern, um mit einer ausgewählten Codierung zu codieren. Hier ist eine Lösung, die ich auf Python: Wie wird sys.stdout.encoding ausgewählt? , insbesondere ein Kommentar von "toka":

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

Möglicherweise möchten Sie versuchen, die Umgebungsvariable "PYTHONIOENCODING" in "utf_8" zu ändern. Ich habe eine Seite über meine Tortur mit diesem Problem geschrieben .

Tl; dr des Blogposts:

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))

gibt Ihnen

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

erledige den Job, kann ihn aber nicht auf python selbst setzen ...

was wir tun können, ist zu überprüfen, ob keine Einstellung vorhanden ist, und den Benutzer anzuweisen, diese Einstellung vorzunehmen, bevor das Skript aufgerufen wird:

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)

Aktualisieren Sie, um auf den Kommentar zu antworten: Das Problem tritt nur bei der Weiterleitung an stdout auf. Ich habe in Fedora 25 Python 2.7.13 getestet

python --version
Python 2.7.13

katze b.py

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

print sys.stdout.encoding

laufen ./b.py

UTF-8

laufen ./b.py | geringer, weniger

None
60
Sérgio

Ich hatte ein ähnliches Problem letzte Woche . Es war einfach, in meinem IDE (PyCharm) zu beheben.

Hier war meine Lösung:

Ausgehend von der PyCharm-Menüleiste: Datei -> Einstellungen ... -> Editor -> Dateicodierungen, stellen Sie dann "IDE-Codierung", "Projektcodierung" und "Standardcodierung für Eigenschaftendateien" auf ALLES in UTF-8 ein und sie funktioniert jetzt wie ein Zauber.

Hoffe das hilft!

5
CLaFarge

Eine wohl bereinigte Version von Craig McQueens Antwort.

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

Verwendungszweck:

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

Ich könnte es mit einem Aufruf an "automatisieren":

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

Ja, es ist möglich, hier eine Endlosschleife zu erhalten, wenn dieses "setenv" fehlschlägt.

2
jno

Ich bin in einer älteren Anwendung auf dieses Problem gestoßen, und es war schwierig zu identifizieren, wo was gedruckt wurde. Ich habe mir bei diesem Hack geholfen:

# 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)

Oben auf meinem Skript test.py:

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

Beachten Sie, dass dadurch ALLE zu druckenden Aufrufe geändert werden, um eine Codierung zu verwenden, sodass Ihre Konsole Folgendes ausgibt:

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

Ich dachte nur, ich würde hier etwas erwähnen, mit dem ich lange experimentieren musste, bevor ich endlich realisierte, was los war. Dies mag für alle hier so offensichtlich sein, dass sie sich nicht die Mühe gemacht haben, es zu erwähnen. Aber es hätte mir geholfen, wenn sie es getan hätten, also nach diesem Prinzip ...!

NB: Ich verwende speziell Jython , Version 2.7, daher trifft dies möglicherweise nicht auf CPython zu.

NB2: Die ersten beiden Zeilen meiner .py-Datei sind:

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

Der String-Konstruktionsmechanismus "%" (AKA "Interpolationsoperator") verursacht auch ZUSÄTZLICHE Probleme ... Wenn die Standardcodierung der "Umgebung" ASCII ist und Sie versuchen, so etwas zu tun

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

Sie werden in Eclipse keine Schwierigkeiten haben ... In einer Windows-CLI (DOS-Fenster) werden Sie feststellen, dass die Codierung Codepage 85 (mein Windows 7-Betriebssystem) oder etwas Ähnliches ist, das mit Europa zurechtkommt zumindest Zeichen mit Akzent, damit es funktioniert.

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

wird auch funktionieren.

Wenn Sie OTOH über die CLI zu einer Datei weiterleiten, lautet die Standardcodierung None (Keine). Die Standardeinstellung lautet ASCII (auf meinem Betriebssystem ohnehin). Keiner der oben genannten Ausdrucke kann verarbeitet werden. (gefürchteter Codierungsfehler).

Dann könnten Sie daran denken, Ihre Standardausgabe mit umzuleiten

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

und versuchen Sie, die CLI-Piping-Funktion für eine Datei auszuführen ... Seltsamerweise funktioniert Druck A oben ... Druck B oben löst jedoch den Codierungsfehler aus! Folgendes wird jedoch funktionieren:

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

Die Schlussfolgerung, zu der ich gekommen bin (vorläufig), ist, dass, wenn eine Zeichenfolge, die als nicode - Zeichenfolge mit dem Präfix "u" angegeben ist, an den% -Handhabungsmechanismus übergeben wird, dies anscheinend die Verwendung von beinhaltet die Standard-Umgebungskodierung , unabhängig davon, ob Sie stdout auf Weiterleitung eingestellt haben!

Wie die Leute damit umgehen, ist eine Frage der Wahl. Ich würde einen Unicode-Experten begrüßen, der erklärt, warum dies passiert, ob ich es auf irgendeine Weise falsch verstanden habe, was die bevorzugte Lösung dafür ist, ob es auch für CPython gilt, ob es in Python 3 usw. usw.

1
mike rodent

Unter Ubuntu 12.10 und GNOME Terminal wird kein Fehler erzeugt, wenn das Programm auf stdout druckt oder eine Pipe für andere Programme verwendet. Sowohl die Dateicodierung als auch die Terminalcodierung ist TF-8 .

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

Welches Betriebssystem und welchen Terminalemulator verwenden Sie? Ich habe gehört, dass einige meiner Kollegen ähnliche Probleme haben, wenn sie iTerm 2 und OS X verwenden. iTerm 2 kann der Schuldige sein.

Update: Diese Antwort ist falsch - siehe Kommentare für Details

1
Fish Monitor