Python, Unicode und die Windows-Konsole

146

Wenn ich versuche, eine Unicode-Zeichenfolge in einer Windows-Konsole zu drucken, wird eine UnicodeEncodeError: 'charmap' codec can't encode character ....Fehlermeldung angezeigt. Ich gehe davon aus, dass die Windows-Konsole keine Nur-Unicode-Zeichen akzeptiert. Was ist der beste Weg, um das zu umgehen? Gibt es eine Möglichkeit, Python automatisch drucken zu lassen, ?anstatt in dieser Situation zu scheitern?

Bearbeiten: Ich verwende Python 2.5.


Hinweis: Die Antwort von @ LasseV.Karlsen mit dem Häkchen ist veraltet (ab 2008). Bitte verwenden Sie die unten aufgeführten Lösungen / Antworten / Vorschläge mit Vorsicht !!

Die Antwort von @JFSebastian ist ab heute (6. Januar 2016) relevanter.

James Sulak
quelle
Auf welcher Version von Python bist du? Ich habe Hinweise gesehen, dass dies in 2.4.3 gebrochen und in 2.4.4 behoben wurde.
Stu
3
Verwandte: bugs.python.org/issue1602
jfs
Schau dir das an.
Soorena
1
Die einfachste Antwort, die ich gefunden habe, ist die Eingabe von: chcp 65001 vor der Verwendung von pyhton in cmd
Soorena
1
Dann sollten Sie Ihre akzeptierte Antwort ändern ...
Mr_and_Mrs_D

Antworten:

38

Hinweis: Diese Antwort ist veraltet (ab 2008). Bitte verwenden Sie die unten stehende Lösung mit Vorsicht !!


Hier ist eine Seite, auf der das Problem und eine Lösung beschrieben werden (suchen Sie auf der Seite nach dem Text, der sys.stdout in eine Instanz einwickelt ):

PrintFails - Python Wiki

Hier ist ein Code-Auszug von dieser Seite:

$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line'
  UTF-8
  <type 'unicode'> 2
  Б
  Б

  $ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line' | cat
  None
  <type 'unicode'> 2
  Б
  Б

Auf dieser Seite finden Sie weitere Informationen, die es wert sind, gelesen zu werden.

Lasse V. Karlsen
quelle
7
Der Link ist tot und der Kern der Antwort wurde nicht zitiert. -1
0xC0000022L
1
Wenn ich die gegebenen Ratschläge zum Verpacken versuche sys.stdout, werden die falschen Dinge gedruckt. Zum Beispiel u'\u2013'wird ûanstelle eines en-dash.
user2357112 unterstützt Monica
@ user2357112 Dazu müssen Sie eine neue Frage stellen. Unicode und Systemkonsole sind nicht unbedingt die beste Kombination, aber ich weiß nicht genug darüber. Wenn Sie also eine eindeutige Antwort benötigen, stellen Sie hier auf SO eine Frage dazu.
Lasse V. Karlsen
2
Der Link ist tot. Das Codebeispiel ist falsch für die Windows-Konsole, bei der sich die Codepage (OEM) cp437von der Windows ANSI-Codepage wie unterscheidet cp1252. Der Code behebt keinen UnicodeEncodeError: 'charmap' codec can't encode characterFehler und kann zu Mojibake führen, z. B. ا©wird er stillschweigend durch ersetzt ╪º⌐.
JFS
73

Update: Python 3.6 implementiert PEP 528: Ändern der Windows-Konsolencodierung in UTF-8 : Die Standardkonsole unter Windows akzeptiert jetzt alle Unicode-Zeichen. Intern verwendet es dieselbe Unicode-API wie das win-unicode-consoleunten erwähnte Paket . print(unicode_string)sollte jetzt einfach funktionieren.


Ich bekomme eine UnicodeEncodeError: 'charmap' codec can't encode character... Fehlermeldung.

Der Fehler bedeutet, dass Unicode-Zeichen, die Sie drucken möchten, nicht mit der aktuellen ( chcp) Konsolenzeichencodierung dargestellt werden können. Die Codepage ist oft eine 8-Bit-Codierung wie zcp437 , die nur ~ 0x100 Zeichen von ~ 1M Unicode-Zeichen darstellen kann:

>>> u "\ N {EURO SIGN}". encode ('cp437')
Traceback (letzter Anruf zuletzt):
...
UnicodeEncodeError: Der Codec 'charmap' kann das Zeichen '\ u20ac' an Position 0 nicht codieren:
Charakterkarten zu 

Ich gehe davon aus, dass die Windows-Konsole keine Nur-Unicode-Zeichen akzeptiert. Was ist der beste Weg, um das zu umgehen?

Die Windows-Konsole akzeptiert Unicode-Zeichen und kann sie sogar anzeigen (nur BMP), wenn die entsprechende Schriftart konfiguriert ist . WriteConsoleW()Die API sollte wie in der Antwort von @Daira Hopwood vorgeschlagen verwendet werden . Es kann transparent aufgerufen werden, dh Sie müssen und sollten Ihre Skripte nicht ändern, wenn Sie das win-unicode-consolePaket verwenden :

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

Siehe Was ist mit Python 3.4, Unicode, verschiedenen Sprachen und Windows los?

Gibt es eine Möglichkeit, Python automatisch drucken zu lassen, ?anstatt in dieser Situation zu scheitern?

Wenn es ?in Ihrem Fall ausreicht, alle nicht codierbaren Zeichen durch zu ersetzen, können Sie envvar PYTHONIOENCODINGfestlegen :

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

In Python 3.6+ wird die von PYTHONIOENCODINGenvvar angegebene Codierung für interaktive Konsolenpuffer ignoriert, es sei denn, PYTHONLEGACYWINDOWSIOENCODINGenvvar ist auf eine nicht leere Zeichenfolge festgelegt.

jfs
quelle
3
"Die Standardkonsole unter Windows akzeptiert jetzt alle Unicode-Zeichen" ABER Sie müssen die Konsole konfigurieren: Klicken Sie mit der rechten Maustaste auf den oberen Rand des Fensters (des cmd oder des Python-IDLE), wählen Sie in Standard / Schriftart die "Lucida-Konsole". (Japanisch und Chinesisch arbeiten nicht für mich, aber ich sollte ohne es überleben ...)
JinSnow
2
@ Guillaume: Die Antwort enthält den fett gedruckten Satz über die Windows-Konsole: "Wenn die entsprechende Schriftart konfiguriert ist." In dieser Antwort wird IDLE nicht erwähnt, aber Sie müssen die Schriftart nicht konfigurieren (ich sehe japanische und chinesische Zeichen in IDLE standardmäßig in Ordnung. Versuchen Sie print('\u4E01'), print('\u6b63')).
JFS
2
@Guillaume Sie können sogar Chinesisch erhalten, wenn Sie das Sprachpaket in Windows 10 installieren. Es wurden Konsolenschriftarten hinzugefügt, die Chinesisch unterstützen.
Mark Tolonen
28

Trotz der anderen plausibel klingenden Antworten, die darauf hindeuten, die Codepage auf 65001 zu ändern, funktioniert dies nicht . (Außerdem sys.setdefaultencodingist es keine gute Idee , die Standardcodierung mit zu ändern .)

In dieser Frage finden Sie Details und Code, der funktioniert.

Daira Hopwood
quelle
2
win-unicode-consoleMit dem Python-Paket (basierend auf Ihrem Code) können Sie vermeiden, dass Ihr Skript geändert wird, wenn Unicode direkt mit dem py -mrun your_script.pyBefehl gedruckt wird .
JFS
12

Wenn Sie nicht daran interessiert sind, eine zuverlässige Darstellung der fehlerhaften Zeichen zu erhalten, können Sie Folgendes verwenden (Arbeiten mit Python> = 2.6, einschließlich 3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

Die fehlerhaften Zeichen in der Zeichenfolge werden in eine Darstellung konvertiert, die von der Windows-Konsole gedruckt werden kann.

Giampaolo Rodolà
quelle
.encode('utf8').decode(sys.stdout.encoding)führt zu Mojibake zB, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Einfach print(s.encode('utf-8'))kann ein besserer Weg sein, um Compilerfehler zu vermeiden. Stattdessen erhalten Sie eine \ xNN-Ausgabe für nicht druckbare Zeichen, was für meine Diagnosemeldungen ausreichte.
CODE-REaD
4
Das ist enorm, spektakulär falsch. Das Codieren in UTF-8 und das Dekodieren als 8-Bit-Zeichensatz schlägt a) häufig fehl, nicht alle Codepages haben Zeichen für alle 256-Byte-Werte und b) immer die falsche Interpretation der Daten, wodurch ein Mojibake erzeugt wird stattdessen zu Durcheinander führt.
Martijn Pieters
10

Mit dem folgenden Code wird Python auch unter Windows als UTF-8 an die Konsole ausgegeben.

Die Konsole zeigt die Zeichen unter Windows 7 gut an, aber unter Windows XP werden sie nicht gut angezeigt, aber zumindest funktioniert es und vor allem haben Sie auf allen Plattformen eine konsistente Ausgabe Ihres Skripts. Sie können die Ausgabe in eine Datei umleiten.

Der folgende Code wurde mit Python 2.6 unter Windows getestet.


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"
Sorin
quelle
1
Gibt es eine Möglichkeit, dies zu vermeiden, indem Sie einfach eine andere Konsole verwenden?
Endolith
@sorin: Warum machst du zuerst import win32consoleaußerhalb von a tryund später machst du es bedingt innerhalb von a try? Ist das nicht sinnlos (der erste import)
0xC0000022L
Für das, was es wert ist, funktioniert das von David-Sarah Hopwood bereitgestellte (ich habe dieses nicht einmal zum Laufen gebracht, weil ich mir nicht die Mühe gemacht habe, das Win32-Erweiterungsmodul zu installieren)
Jaykul
4
Ändern Sie nicht die Standardcodierung des Systems. Korrigieren Sie stattdessen Ihre Unicode-Werte. Durch Ändern der Standardcodierung können Bibliotheken beschädigt werden, die auf dem Standardverhalten beruhen . Es gibt einen Grund, warum Sie ein erneutes Laden des Moduls erzwingen müssen, bevor Sie dies tun können.
Martijn Pieters
7

Geben Sie einfach diesen Code in die Befehlszeile ein, bevor Sie das Python-Skript ausführen:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
quelle
5

Wie die Antwort von Giampaolo Rodolà, aber noch schmutziger: Ich beabsichtige wirklich, wirklich lange (bald) das gesamte Thema der Codierungen und deren Anwendung auf Windoze-Konsolen zu verstehen.

Im Moment wollte ich nur etwas, was bedeuten würde, dass mein Programm NICHT abstürzt, und was ich verstand ... und das auch nicht das Importieren zu vieler exotischer Module beinhaltete (insbesondere verwende ich Jython, also die Hälfte der Zeit ein Python Modul stellt sich tatsächlich als nicht verfügbar heraus).

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

NB "pr" ist kürzer als "print" (und viel kürzer als "safeprint") ...!

Mike Nagetier
quelle
Clever, ein schneller und schmutziger Weg, um das Problem zu umgehen. Ich denke, das ist großartig für eine intermittierende Lösung.
JFA
3

Für Python 2 versuchen Sie:

print unicode(string, 'unicode-escape')

Für Python 3 versuchen Sie:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

Oder versuchen Sie es mit der Win-Unicode-Konsole:

pip install win-unicode-console
py -mrun your_script.py
shubaly
quelle
2

TL; DR:

print(yourstring.encode('ascii','replace'));

Ich bin selbst darauf gestoßen und habe an einem Twitch-Chat-Bot (IRC) gearbeitet. (Python 2.7 spätestens)

Ich wollte Chat-Nachrichten analysieren, um zu antworten ...

msg = s.recv(1024).decode("utf-8")

Drucken Sie sie aber auch sicher in einem für Menschen lesbaren Format auf die Konsole:

print(msg.encode('ascii','replace'));

Dies behebt das Problem, dass der Bot UnicodeEncodeError: 'charmap'Fehler auslöst, und ersetzt die Unicode-Zeichen durch ?.

Matthew Estock
quelle
2

Die Ursache Ihres Problems ist NICHT, dass die Win-Konsole nicht bereit ist, Unicode zu akzeptieren (da dies der Fall ist, da ich Win2k standardmäßig schätze). Dies ist die Standardsystemcodierung. Probieren Sie diesen Code aus und sehen Sie, was er Ihnen bietet:

import sys
sys.getdefaultencoding()

Wenn es ASCII heißt, gibt es deine Ursache ;-) Du musst eine Datei namens sitecustomize.py erstellen und sie unter den Python-Pfad stellen (ich habe sie unter /usr/lib/python2.5/site-packages abgelegt, aber das ist anders Win - es ist c: \ python \ lib \ site-packages oder so) mit folgendem Inhalt:

import sys
sys.setdefaultencoding('utf-8')

und vielleicht möchten Sie auch die Codierung in Ihren Dateien angeben:

# -*- coding: UTF-8 -*-
import sys,time

Bearbeiten: Weitere Informationen finden Sie im Dive in Python-Buch

Bartosz Radaczyński
quelle
2
setdefaultencoding () ist in sys nicht länger (ab v2.0 gemäß den Moduldokumenten).
Jon Cage
Ich kann es momentan nicht beweisen, aber ich weiß, dass ich diesen Trick in einer späteren Version verwendet habe - 2.5 unter Windows.
Bartosz Radaczyński
6
OK, nach einiger Zeit habe ich Folgendes herausgefunden: "Diese Funktion ist nur für die Implementierung des Site-Moduls und bei Bedarf für die Site-Anpassung vorgesehen. Sobald sie vom Site-Modul verwendet wird, wird sie aus dem Namespace des Sys-Moduls entfernt. ""
Bartosz Radaczyński
4
Tatsächlich können Sie die Windows-Konsole auf utf-8 einstellen. Sie müssen chcp 65001 sagen und es wird Unicode sein.
Bartosz Radaczyński
4
Um es absolut klar zu machen: Es ist eine sehr schlechte Idee , die Standardkodierung zu ändern. Dies ist vergleichbar mit dem Spalking Ihres gebrochenen Beins und dem Weitergehen, als wäre nichts passiert, anstatt dass ein Arzt den Knochen richtig setzt. Der gesamte Code, der Unicode-Text verarbeitet, sollte dies konsistent tun, anstatt sich auf implizite Codierung / Decodierung zu verlassen.
Martijn Pieters
1

Ein bisschen verwandt mit der Antwort von JF Sebastian, aber direkter.

Wenn beim Drucken auf die Konsole / das Terminal dieses Problem auftritt, gehen Sie wie folgt vor:

>set PYTHONIOENCODING=UTF-8
Kinjal Dixit
quelle
3
set PYTHONIOENCODING=UTF-8kann zu Mojibake führen, wenn die Konsole eine andere Codierung wie cp437 verwendet. cp65001hat verschiedene Probleme . Um Unicode auf der Windows-Konsole zu drucken, sollte die Unicode-API verwendet werden ( WriteConsoleW()), wie in meiner Antwort vorgeschlagen. Dabei PYTHONIOENCODINGwerden nur Zeichen ersetzt, die in der aktuellen OEM-Codepage nicht dargestellt werden können ?( WriteConsoleW()funktioniert auch für solche Zeichen). PYTHONIOENCODINGkann verwendet werden, wenn die Ausgabe in eine Datei umgeleitet wird.
JFS
1

Python 3.6 Windows 7: Es gibt verschiedene Möglichkeiten, eine Python zu starten. Sie können die Python-Konsole (auf der sich ein Python-Logo befindet) oder die Windows-Konsole (auf der cmd.exe steht) verwenden.

Ich konnte keine utf8-Zeichen in der Windows-Konsole drucken. Das Drucken von utf-8-Zeichen wirft mir diesen Fehler:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

Nachdem ich versucht hatte, die obige Antwort zu verstehen, stellte ich fest, dass es sich nur um ein Einstellungsproblem handelte. Klicken Sie mit der rechten Maustaste oben in den Fenstern der cmd-Konsole und wählen Sie auf der Registerkarte fontlucida console aus.

J. Does
quelle
0

James Sulak fragte:

Gibt es eine Möglichkeit, Python automatisch drucken zu lassen? anstatt in dieser Situation zu scheitern?

Andere Lösungen empfehlen, dass wir versuchen, die Windows-Umgebung zu ändern oder die Python- print()Funktion zu ersetzen . Die folgende Antwort kommt der Erfüllung der Anfrage von Sulak näher.

Unter Windows 7 kann Python 3.5 dazu gebracht werden, Unicode zu drucken, ohne UnicodeEncodeErrorFolgendes zu werfen :

    Anstelle von:    print(text)
    Ersatz:     print(str(text).encode('utf-8'))

Anstatt eine Ausnahme auszulösen, zeigt Python jetzt nicht druckbare Unicode-Zeichen als \ xNN- Hex-Codes an, z.

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait plus qu \ xe2 \ x80 \ x99un point noir

Anstatt

  Halmalo n'était plus qu'un point noir

Zugegeben, letzteres ist ceteris paribus vorzuziehen , ansonsten ist Ersteres für diagnostische Meldungen völlig korrekt. Da Unicode als Literalbytewerte angezeigt wird, kann ersteres auch bei der Diagnose von Codierungs- / Decodierungsproblemen hilfreich sein.

Hinweis: Der str()obige Aufruf ist erforderlich, da encode()Python andernfalls ein Unicode-Zeichen als Tupel von Zahlen ablehnt.

CODE-REaD
quelle