Layer 8Ehrlicher Tech-Support
SoftwareSchweregrad: LästigUnicodeEncodeError

Python: UnicodeEncodeError beim Schreiben und Ausgeben

UnicodeEncodeError in Python: Umlaute oder Emojis passen nicht in die Zielkodierung. So legst du sie für Dateien und Konsole ausdrücklich fest.

Aufwand:
ca. 15 Min.
Voraussetzung:
Python 3, Editor, Terminal oder Eingabeaufforderung
Gilt für:
Python 3 (alle aktuellen Versionen)

Das Symptom

Das Skript rechnet fehlerfrei, sammelt brav seine Daten – und stirbt in der letzten Zeile, ausgerechnet beim Hinausschreiben:

UnicodeEncodeError: 'charmap' codec can't encode character '€' in position 12: character maps to <undefined>

Mal trifft es f.write(...), mal ein simples print() auf der Windows-Konsole. Auf dem eigenen Rechner lief dasselbe Skript gestern noch durch.

Die „Layer 8“-Wahrheit

Zwei Fehler mit fast gleichem Namen, und trotzdem sind es zwei verschiedene Baustellen. Beim UnicodeDecodeError kommen Bytes herein, die nicht zur angenommenen Kodierung passen – ein Lesefehler, die Datei ist heil, die Annahme war falsch. Beim UnicodeEncodeError geht es genau andersherum: Dein Text ist völlig in Ordnung, er soll nur in eine Kodierung gepresst werden, die das betreffende Zeichen nicht kennt. Decode am Eingang, Encode am Ausgang. Wer diese Richtung einmal sauber im Kopf hat, sucht nie wieder an der falschen Stelle.

Der eigentliche Ärger entsteht dadurch, dass Python die Kodierung stillschweigend von der Umgebung erbt. Die Dokumentation zu open() sagt es ohne Umschweife: Der Standard ist plattformabhängig, nämlich das, was locale.getencoding() zurückgibt. Auf dem Entwicklungsrechner ist das UTF-8, auf dem Zielsystem vielleicht eine ältere Codepage, in der es kein Euro-Zeichen und ganz sicher kein Emoji gibt. Derselbe Code, dieselben Daten, unterschiedlicher Ausgang – der Klassiker hinter dem Satz „bei mir lief es doch“.

Die Reparatur ist deshalb keine Fehlerbehandlung, sondern eine Entscheidung: Kodierung hinschreiben, statt sie sich vom Betriebssystem zuflüstern zu lassen. Für Dateien ist das encoding='utf-8' beim Öffnen, für die Konsole PYTHONIOENCODING, für alles auf einmal der UTF-8-Modus über -X utf8 beziehungsweise PYTHONUTF8=1. Und die verlockende Abkürzung errors='ignore' ist keine Lösung, sondern ein Trick, mit dem man sich das Problem aus dem Blickfeld schafft: Die Dokumentation warnt ausdrücklich, dass das Ignorieren von Kodierfehlern zu Datenverlust führen kann. Das Programm läuft dann durch, die Zeichen sind trotzdem weg – nur beschwert sich niemand mehr darüber.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Die Richtung des Fehlers feststellen

    Steht im Traceback UnicodeEncodeError, hast du geschrieben oder ausgegeben – nicht gelesen. Die Meldung nennt den überforderten Codec, das Zeichen und seine Position. Das ist bereits die halbe Diagnose: Der Text ist in Ordnung, die Zielkodierung kann ihn nur nicht darstellen. Welche Kodierung dein Ausgabekanal gerade benutzt, verrät dieser Einzeiler. Für den Fehler in die andere Richtung gibt es eine eigene Seite in dieser Sammlung.

    python3 -c "import sys; print(sys.stdout.encoding)"
    
  2. 2.

    Beim Öffnen einer Datei die Kodierung ausdrücklich angeben

    Ohne encoding-Angabe nimmt Python laut Dokumentation eine plattformabhängige Vorgabe, nämlich die des Locale. Wer die Kodierung hinschreibt, macht das Programm auf jedem Rechner gleich – und UTF-8 kann jedes Zeichen darstellen, das du im Text haben kannst.

    with open('bericht.txt', 'w', encoding='utf-8') as f:
        f.write('Grüße aus Köln 🎉')
    
  3. 3.

    Für die Standardausgabe PYTHONIOENCODING setzen

    Bricht nicht das Schreiben in eine Datei ab, sondern ein print auf die Konsole, liegt es am Kanal, nicht an deinem Code. Diese Umgebungsvariable überschreibt laut Dokumentation die Kodierung für stdin, stdout und stderr, in der Form encodingname:errorhandler. Für stderr wird der Fehlerbehandler dabei ignoriert – dort gilt immer backslashreplace.

    # Linux, macOS
    PYTHONIOENCODING=utf-8 python3 skript.py
    
    # Windows, Eingabeaufforderung
    set PYTHONIOENCODING=utf-8
    python skript.py
    
  4. 4.

    Den UTF-8-Modus einschalten, statt jede Stelle einzeln zu reparieren

    Python kennt einen eigenen UTF-8-Modus. Die Dokumentation nennt zwei Wege, ihn zu aktivieren: die Option -X utf8 beim Aufruf oder die Umgebungsvariable PYTHONUTF8 mit dem Wert 1. Mit -X utf8=0 beziehungsweise PYTHONUTF8=0 wird er ausdrücklich abgeschaltet. Das ist der große Hebel für Altbestand, den man nicht Zeile für Zeile anfassen will.

    python3 -X utf8 skript.py
    
    # dauerhaft für die Sitzung (Windows, Eingabeaufforderung)
    set PYTHONUTF8=1
    
  5. 5.

    errors= nur bewusst und mit Nebenwirkung im Blick wählen

    Der Standard ist strict und bricht ab – das ist gut so, denn es zeigt dir das Problem. Die anderen Handler sind Notlösungen mit Preisschild: ignore lässt Zeichen ersatzlos verschwinden (die Dokumentation warnt hier ausdrücklich vor Datenverlust), replace setzt ein Ersatzzeichen, backslashreplace schreibt Escape-Sequenzen, xmlcharrefreplace und namereplace sind laut Dokumentation nur beim Schreiben unterstützt. Keiner davon macht das Zeichen in der Zieldatei wieder lesbar.

    # läuft durch, verändert aber die Ausgabe - bewusst einsetzen
    open('bericht.txt', 'w', encoding='cp1252', errors='backslashreplace')
    
  6. 6.

    Prüfen, ob das Ziel die Zeichen überhaupt kann

    Wenn eine fremde Schnittstelle zwingend eine alte Codepage verlangt, ist der Fehler kein Bug, sondern eine Aussage: Das Zeichen existiert dort nicht. Dann gehört die Entscheidung nach vorn – Zeichen vorher ersetzen (etwa das Euro-Zeichen durch EUR), statt sie hinten still verschwinden zu lassen. Ein kurzer Test zeigt, was die Zielkodierung trägt.

    python3 -c "print('🎉'.encode('cp1252', errors='backslashreplace'))"
    

Häufige Fragen (FAQ)

?Was ist der Unterschied zum UnicodeDecodeError?

Die Richtung. Decodieren heißt: Bytes von der Platte werden zu Text – das passiert beim Lesen, und dort scheitert es, wenn die Bytes nicht zur angenommenen Kodierung passen. Encodieren heißt: Text im Speicher wird zu Bytes – das passiert beim Schreiben in eine Datei oder beim Ausgeben auf die Konsole, und dort scheitert es, wenn das Zeichen in der Zielkodierung schlicht nicht existiert. Merksatz: Decode am Eingang, Encode am Ausgang. Beim Decode-Fehler ist die Datei in Ordnung und deine Annahme falsch; beim Encode-Fehler ist dein Text in Ordnung und das Ziel zu eng. Ein cp1252-Ausgabekanal kennt kein Emoji – da hilft keine Fehlerbehandlung, sondern nur eine Kodierung, die das Zeichen darstellen kann.

?Warum knallt es nur auf Windows oder nur im Terminal des Kollegen?

Weil Python die Kodierung nicht erfindet, sondern von der Umgebung übernimmt. Die Dokumentation zu open() sagt es unmissverständlich: Wird encoding nicht angegeben, ist die verwendete Kodierung plattformabhängig – nämlich das, was locale.getencoding() zurückgibt. Auf dem einen Rechner ist das UTF-8 und alles läuft, auf dem anderen eine ältere Codepage, in der ein einzelnes Sonderzeichen nicht vorkommt. Derselbe Code, dieselben Daten, unterschiedliches Ergebnis. Genau deshalb gehört die Kodierung in den Code und nicht in den Zufall der Umgebung.

?Kann ich nicht einfach errors='ignore' setzen und Ruhe haben?

Ruhe ja, Daten nein. Die Python-Dokumentation vermerkt zu diesem Handler ausdrücklich, dass das Ignorieren von Kodierfehlern zu Datenverlust führen kann – die betroffenen Zeichen verschwinden ersatzlos, und niemand sieht es der Ausgabedatei später an. Wenn ein Schreibvorgang gar nicht scheitern darf, ist backslashreplace die ehrlichere Wahl: Das Zeichen bleibt als Escape-Sequenz sichtbar und damit rekonstruierbar. Für Dateien, die weiterverarbeitet werden, ist aber ohnehin UTF-8 die richtige Antwort und keine Fehlerbehandlung.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästigMixed Content: The page at … was loaded over HTTPS, but requested an insecure … This request has been blocked; the content must be served over HTTPS.

Sichere Seite bleibt halb leer: Gemischte Inhalte werden blockiert

Die Seite läuft über HTTPS, lädt aber Bilder oder Skripte per HTTP nach. Der Browser blockiert sie — und die halbe Seite fehlt. Warum das gut ist.