webentwicklung-frage-antwort-db.com.de

UnicodeEncodeError: Der 'Charmap'-Codec kann nicht codiert werden - Zeichenzuordnungen zu <undefiniert>, Druckfunktion

Ich schreibe ein Python (Python 3.3) -Programm, um einige Daten mithilfe der POST -Methode an eine Webseite zu senden. Hauptsächlich für den Debug-Vorgang erhalte ich das Seitenergebnis und Anzeige auf dem Bildschirm mit der Funktion print().

Der Code ist wie folgt:

conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));

die HTTPResponse.read() -Methode gibt ein bytes -Element zurück, das die Seite codiert (ein gut formatiertes UTF-8-Dokument). Es schien in Ordnung zu sein, bis ich IDLE GUI für Windows und nicht mehr verwendete benutzte stattdessen die Windows-Konsole. Die zurückgegebene Seite hat ein U + 2014-Zeichen (Bindestrich), das die Druckfunktion in der Windows-GUI gut übersetzt (ich nehme an, Codepage 1252), jedoch nicht in der Windows-Konsole (Codepage 850). Aufgrund des Standardverhaltens von strict erhalte ich die folgende Fehlermeldung:

UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>

Ich könnte es mit diesem ziemlich hässlichen Code beheben:

print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))

Jetzt wird das anstößige Zeichen "-" durch ein ? Ersetzt. Nicht der ideale Fall (ein Bindestrich sollte ein besserer Ersatz sein), aber gut genug für meinen Zweck.

Es gibt einige Dinge, die ich an meiner Lösung nicht mag.

  1. Der Code ist hässlich bei all dem Dekodieren, Kodieren und Dekodieren.
  2. Es löst das Problem nur für diesen Fall. Wenn ich das Programm für ein System portiere, das eine andere Codierung verwendet (latin-1, cp437, zurück zu cp1252 usw.), sollte es die Zielcodierung erkennen. Es tut nicht. (Wenn Sie beispielsweise die IDLE-GUI erneut verwenden, geht auch der Emdash verloren, was vorher nicht passiert ist.)
  3. Es wäre schöner, wenn der Emdash in einen Bindestrich anstatt in einen Befragungsknall übersetzt würde.

Das Problem ist nicht der Emdash (ich kann mir verschiedene Möglichkeiten vorstellen, um dieses spezielle Problem zu lösen), sondern ich muss robusten Code schreiben. Ich füttere die Seite mit Daten aus einer Datenbank und diese Daten können zurückkommen. Ich kann viele andere widersprüchliche Fälle vorwegnehmen: Ein 'Á' U + 00c1 (was in meiner Datenbank möglich ist) könnte in CP-850 (DOS/Windows-Konsolencodierung für westeuropäische Sprachen) übersetzt werden, nicht jedoch in CP-437 (Codierung für USA) Englisch (Standardeinstellung bei vielen Windows-Installationen).

Also die Frage:

Gibt es eine schönere Lösung, die meinen Code von der Codierung der Ausgabeschnittstelle unabhängig macht?

Ich sehe drei Lösungen dafür:

  1. Ändern Sie die Ausgabecodierung, damit immer UTF-8 ausgegeben wird. Siehe z. Festlegen der richtigen Codierung beim Weiterleiten von stdout in Python , aber ich konnte dieses Beispiel nicht zum Laufen bringen.

  2. Der folgende Beispielcode macht die Ausgabe auf Ihren Zielzeichensatz aufmerksam.

    # -*- coding: utf-8 -*-
    import sys
    
    print sys.stdout.encoding
    print u"Stöcker".encode(sys.stdout.encoding, errors='replace')
    print u"Стоескер".encode(sys.stdout.encoding, errors='replace')
    

    In diesem Beispiel werden nicht druckbare Zeichen in meinem Namen ordnungsgemäß durch ein Fragezeichen ersetzt.

    Wenn Sie eine benutzerdefinierte Druckfunktion erstellen, z. genannt myprint, mit diesen Mechanismen, um die Ausgabe richtig zu codieren, können Sie einfach print durch myprint ersetzen, wo immer dies erforderlich ist, ohne dass der gesamte Code hässlich aussieht.

  3. Setzen Sie die Ausgabecodierung zu Beginn der Software global zurück:

    Die Seite http://www.macfreek.nl/memory/Encoding_of_Python_stdout bietet eine gute Übersicht, wie Sie die Ausgabecodierung ändern können. Besonders der Abschnitt "StreamWriter Wrapper around Stdout" ist interessant. Im Wesentlichen heißt es, die E/A-Codierungsfunktion wie folgt zu ändern:

    In Python 2:

    if sys.stdout.encoding != 'cp850':
      sys.stdout = codecs.getwriter('cp850')(sys.stdout, 'strict')
    if sys.stderr.encoding != 'cp850':
      sys.stderr = codecs.getwriter('cp850')(sys.stderr, 'strict')
    

    In Python 3:

    if sys.stdout.encoding != 'cp850':
      sys.stdout = codecs.getwriter('cp850')(sys.stdout.buffer, 'strict')
    if sys.stderr.encoding != 'cp850':
      sys.stderr = codecs.getwriter('cp850')(sys.stderr.buffer, 'strict')
    

    Bei der CGI-Ausgabe von HTML können Sie 'strict' durch 'xmlcharrefreplace' ersetzen, um HTML-codierte Tags für nicht druckbare Zeichen zu erhalten.

    Sie können die Ansätze jederzeit ändern und unterschiedliche Codierungen festlegen. Beachten Sie, dass die Ausgabe nicht angegebener Daten immer noch nicht funktioniert. Alle Daten, Eingaben und Texte müssen daher korrekt in Unicode konvertierbar sein:

    # -*- coding: utf-8 -*-
    import sys
    import codecs
    sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
    print u"Stöcker"                # works
    print "Stöcker".decode("utf-8") # works
    print "Stöcker"                 # fails
    
99
Dirk Stöcker

Basierend auf der Antwort von Dirk Stöcker finden Sie hier eine übersichtliche Wrapper-Funktion für die Druckfunktion von Python 3). Verwenden Sie sie so, als würden Sie print verwenden.

Als zusätzlichen Bonus wird Ihr Text im Vergleich zu den anderen Antworten aufgrund des letzten Dekodierungsschritts nicht als Bytearray ("b" content "), sondern als normale Zeichenfolge (" content ") gedruckt.

def uprint(*objects, sep=' ', end='\n', file=sys.stdout):
    enc = file.encoding
    if enc == 'UTF-8':
        print(*objects, sep=sep, end=end, file=file)
    else:
        f = lambda obj: str(obj).encode(enc, errors='backslashreplace').decode(enc)
        print(*map(f, objects), sep=sep, end=end, file=file)

uprint('foo')
uprint(u'Antonín Dvořák')
uprint('foo', 'bar', u'Antonín Dvořák')
28
Jelle Fresen

Zum Debuggen können Sie print(repr(data)) verwenden.

Drucken Sie zum Anzeigen von Text immer Unicode. Codieren Sie die Zeichencodierung Ihrer Umgebung nicht hart wie cp850 in Ihrem Skript. Informationen zum Dekodieren der http-Antwort finden Sie unter Ein guter Weg, um den Zeichensatz/die Kodierung einer HTTP-Antwort in Python abzurufen .

Zum Drucken von Unicode in die Windows-Konsole können Sie mit win-unicode-console package .

25
jfs

Ich habe mich eingehender damit befasst und herausgefunden, dass die besten Lösungen hier sind.

http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python

In meinem Fall löste ich "UnicodeEncodeError: 'charmap' Codec kann Zeichen nicht codieren"

ursprünglicher Code:

print("Process lines, file_name command_line %s\n"% command_line))

Neuer Code:

print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))  
18
leemonq

Wenn Sie die Windows-Befehlszeile zum Drucken der Daten verwenden, sollten Sie verwenden

chcp 65001

Das hat bei mir funktioniert!

15
Željko Krnjić

Wenn Sie Python 3.6 (möglicherweise 3.5 oder höher) verwenden, wird mir dieser Fehler nicht mehr angezeigt. Ich hatte ein ähnliches Problem, da ich v3.4 verwendet habe, aber es ging weg nachdem ich deinstalliert und neu installiert habe.

1
Solumyr