Ändern der Standardcodierung von Python?

143

Ich habe viele Probleme mit Python, bei denen ich meine Anwendungen von der Konsole aus ausführen kann. Aber in der Eclipse PyDev IDE ist die Standardzeichenkodierung auf UTF-8 eingestellt , und mir geht es gut.

Ich habe nach der Einstellung der Standardcodierung gesucht und die Leute sagen, dass Python die sys.setdefaultencodingFunktion beim Start löscht und wir sie nicht verwenden können.

Was ist die beste Lösung dafür?

Ali Nadalizadeh
quelle
1
Siehe den Blog-Beitrag The Illusive setdefaultencoding .
DJC
3
The best solution is to learn to use encode and decode correctly instead of using hacks.Dies war mit python2 sicherlich möglich, und zwar auf Kosten der ständigen Erinnerung daran / der konsequenten Verwendung Ihrer eigenen Benutzeroberfläche. Meine Erfahrung zeigt, dass dies sehr problematisch wird, wenn Sie Code schreiben, mit dem Sie sowohl mit Python2 als auch mit Python3 arbeiten möchten.
Att Righ

Antworten:

159

Hier ist eine einfachere Methode (Hack), mit der Sie die setdefaultencoding()Funktion zurückerhalten, aus der gelöscht wurde sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Hinweis für Python 3.4+: reload()befindet sich in der importlibBibliothek.)

Dies ist jedoch keine sichere Sache : Dies ist offensichtlich ein Hack, da er sys.setdefaultencoding()absichtlich entfernt wird, syswenn Python gestartet wird. Durch erneutes Aktivieren und Ändern der Standardcodierung kann Code beschädigt werden, bei dem ASCII als Standard verwendet wird (dieser Code kann von Drittanbietern stammen, was das Beheben im Allgemeinen unmöglich oder gefährlich machen würde).

Eric O Lebigot
quelle
5
Ich habe abgelehnt, weil diese Antwort beim Ausführen vorhandener Anwendungen nicht hilfreich ist (was eine Möglichkeit ist, die Frage zu interpretieren). Sie ist falsch, wenn Sie eine Anwendung schreiben / warten, und gefährlich, wenn Sie eine Bibliothek schreiben. Der richtige Weg ist das Festlegen LC_CTYPE(oder in einer Anwendung prüfen, ob es richtig eingestellt ist, und mit einer aussagekräftigen Fehlermeldung abbrechen).
ibotty
@ibotty Ich stimme zu, dass diese Antwort ein Hack ist und dass es gefährlich ist, sie zu verwenden. Es beantwortet jedoch die Frage ("Ändern der Standardcodierung von Python?"). Haben Sie einen Verweis auf die Auswirkung der Umgebungsvariablen LC_CTYPE auf den Python-Interpreter?
Eric O Lebigot
Nun, es wurde nicht erwähnt, es ist zunächst ein Hack. Ansonsten sind gefährliche Antworten, die nicht erwähnt werden, nicht hilfreich.
ibotty
1
@EOL du hast recht. Es wirkt sich jedoch auf die bevorzugte LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
Codierung aus
1
@ user2394901 Von der Verwendung von sys.setdefaultencoding () wurde immer abgeraten !! Und die Codierung von py3k ist fest mit "utf-8" verbunden, und das Ändern dieser Codierung führt zu einem Fehler.
Marlon Abeykoon
70

Wenn Sie diese Fehlermeldung erhalten, wenn Sie versuchen, die Ausgabe Ihres Skripts weiterzuleiten / umzuleiten

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Exportieren Sie einfach PYTHONIOENCODING in die Konsole und führen Sie dann Ihren Code aus.

export PYTHONIOENCODING=utf8

Iman
quelle
3
Dies ist die einzige Lösung, die für mich einen Unterschied gemacht hat. - Ich bin auf Debian 7 mit defekten Gebietsschemaeinstellungen. Vielen Dank.
Pryo
4
Stellen LC_CTYPEsinnvoll , anstatt auf etwas. Es macht auch alle anderen Programme glücklich.
ibotty
5
Ein größerer Fehler in Python3 ist, dass dies PYTHONIOENCODING=utf8nicht die Standardeinstellung ist. Dies macht Skripte brechen nur, weilLC_ALL=C
Tino
Set LC_CTYPE to something sensible insteadDies ist ein vernünftiger Vorschlag. Dies funktioniert nicht so gut, wenn Sie versuchen, Code zu verteilen, der nur auf dem System einer anderen Person funktioniert.
Att Righ
Debian- und Redhat-Betriebssysteme verwenden ein C.utf8Gebietsschema, um vernünftiger zu sein. C. glibc upstream arbeitet daran, es hinzuzufügen. Vielleicht sollten wir Python nicht beschuldigen, die Gebietsschemaeinstellungen zu respektieren \…?
Arthur2e5
52

A) So steuern Sie die sys.getdefaultencoding()Ausgabe:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

Dann

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

und

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Sie könnten Ihre sitecustomize.py höher in Ihre setzen PYTHONPATH.

Vielleicht reload(sys).setdefaultencodingmöchten Sie es auch mit @EOL versuchen

B) Um zu steuern stdin.encodingund stdout.encodingSie möchten einstellen PYTHONIOENCODING:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Dann

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Endlich: Sie können A) oder B) oder beides verwenden!

lukmdo
quelle
(nur Python2) getrennt, aber interessant ist oben mit from __future__ import unicode_literalssiehe Diskussion
lukmdo
17

Ab PyDev 3.4.1 wird die Standardkodierung nicht mehr geändert. Siehe dieses Ticket für Details.

Bei früheren Versionen besteht eine Lösung darin, sicherzustellen, dass PyDev nicht mit UTF-8 als Standardcodierung ausgeführt wird. Führen Sie unter Eclipse Dialogeinstellungen aus ("Konfigurationen ausführen", wenn ich mich richtig erinnere). Sie können die Standardcodierung auf der allgemeinen Registerkarte auswählen. Ändern Sie es in US-ASCII, wenn Sie diese Fehler "früh" haben möchten (mit anderen Worten: in Ihrer PyDev-Umgebung). Lesen Sie auch einen Original-Blogbeitrag zu dieser Problemumgehung .

ChristopheD
quelle
1
Danke Chris. Insbesondere angesichts des obigen Kommentars von Mark T scheint Ihre Antwort für mich am besten geeignet zu sein. Und für jemanden, der nicht in erster Linie ein Eclipse / PyDev-Benutzer ist, hätte ich das nie alleine herausgefunden.
Sean
Ich möchte dies global ändern (anstatt einmal pro Laufkonfiguration
Tim Diggins
13

In Bezug auf Python2 (und nur Python2) basieren einige der früheren Antworten auf der Verwendung des folgenden Hacks:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

Es wird davon abgeraten, es zu verwenden (überprüfen Sie dies oder das )

In meinem Fall hat dies einen Nebeneffekt: Ich verwende Ipython-Notizbücher, und sobald ich den Code ausgeführt habe, funktioniert die Funktion "Drucken" nicht mehr. Ich denke, es würde eine Lösung dafür geben, aber ich denke trotzdem, dass die Verwendung des Hacks nicht die richtige Option sein sollte.

Nachdem ich viele Optionen ausprobiert hatte, verwendete die für mich funktionierende den gleichen Code in der sitecustomize.py, in der sich dieser Code befinden soll . Nach der Auswertung dieses Moduls wird die Funktion setdefaultencoding aus sys entfernt.

Die Lösung besteht also darin, /usr/lib/python2.7/sitecustomize.pyden Code anzuhängen :

import sys
sys.setdefaultencoding('UTF8')

Wenn ich virtualenvwrapper verwende, ist die von mir bearbeitete Datei ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

Und wenn ich es mit Python-Notizbüchern und Conda verwende, ist es das auch ~/anaconda2/lib/python2.7/sitecustomize.py

kiril
quelle
8

Es gibt einen aufschlussreichen Blog-Beitrag darüber.

Siehe https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Ich paraphrasiere seinen Inhalt unten.

In Python 2, das in Bezug auf die Codierung von Zeichenfolgen nicht so stark typisiert war, konnten Sie Operationen an unterschiedlich codierten Zeichenfolgen ausführen und erfolgreich sein. ZB würde das Folgende zurückkehren True.

u'Toshio' == 'Toshio'

Dies würde für jede (normale, nicht vorfixierte) Zeichenfolge gelten, in sys.getdefaultencoding()der standardmäßig codiert wurde , für asciiandere jedoch nicht.

Die Standardcodierung sollte systemweit geändert werden site.py, jedoch nicht an einer anderen Stelle. Die Hacks (auch hier vorgestellt) zum Festlegen in Benutzermodulen waren genau das: Hacks, nicht die Lösung.

Python 3 hat die Systemcodierung standardmäßig auf utf-8 geändert (wenn LC_CTYPE Unicode-fähig ist), aber das grundlegende Problem wurde mit der Anforderung gelöst, "Byte" -Strings explizit zu codieren, wenn sie mit Unicode-Strings verwendet werden.

ibotty
quelle
4

Erstens: Das reload(sys)Festlegen einer zufälligen Standardcodierung nur in Bezug auf die Notwendigkeit eines Ausgabe-Terminal-Streams ist eine schlechte Praxis. reloadändert häufig Dinge in sys, die je nach Umgebung eingerichtet wurden - z. B. sys.stdin / stdout-Streams, sys.excepthook usw.

Lösen des Codierungsproblems auf stdout

Die beste Lösung, die ich kenne, um das Codierungsproblem zu lösen print, Unicode-Strings und strBeyond-ASCIIs (z. B. aus Literalen) auf sys.stdout zu lösen, ist: sich um ein sys.stdout (dateiähnliches Objekt) zu kümmern, das fähig ist und optional tolerant in Bezug auf die Bedürfnisse:

  • Wenn sys.stdout.encodinges Noneaus irgendeinem Grund oder nicht vorhanden oder fälschlicherweise falsch oder "weniger" ist als das, wozu das Standardterminal oder der Standard-Stream wirklich in der Lage ist, versuchen Sie, ein korrektes .encodingAttribut bereitzustellen . Zuletzt durch Ersetzen sys.stdout & sys.stderrdurch ein übersetzendes dateiähnliches Objekt.

  • Wenn das Terminal / der Stream immer noch nicht alle vorkommenden Unicode-Zeichen codieren kann und Sie diese nicht printnur aus diesem Grund unterbrechen möchten , können Sie ein Verhalten zum Codieren mit Ersetzen in das übersetzungsdateiähnliche Objekt einführen.

Hier ein Beispiel:

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

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

Verwenden von einfachen String-Literalen jenseits von ASCII in Python 2/2 + 3-Code

Der einzige gute Grund, die globale Standardcodierung (nur auf UTF-8) zu ändern, betrifft meiner Meinung nach eine Entscheidung über den Quellcode der Anwendung - und nicht aufgrund von Problemen mit der Codierung von E / A-Streams: Zum Schreiben von String-Literalen über ASCII hinaus in Code, ohne dazu gezwungen zu werden um immer u'string'Style-Unicode-Escape zu verwenden. Dies kann ziemlich konsistent erfolgen (trotz des Artikels von anonbadger ), indem eine Python 2- oder Python 2 + 3-Quellcodebasis verwendet wird, die konsistente ASCII- oder UTF-8-Zeichenfolgenliterale konsistent verwendet - sofern diese Zeichenfolgen möglicherweise stumm geschaltet werden Unicode-Konvertierung und zwischen Modulen wechseln oder möglicherweise zu stdout wechseln. Dafür lieber "# encoding: utf-8"oder ascii (keine Deklaration). Ändern oder Löschen von Bibliotheken, die sich immer noch auf sehr dumme Weise auf ASCII-Standardcodierungsfehler jenseits von chr # 127 stützen (was heute selten ist).

Und tun Sie dies beim Start der Anwendung (und / oder über sitecustomize.py) zusätzlich zu dem SmartStdoutoben beschriebenen Schema - ohne Folgendes zu verwenden reload(sys):

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

Auf diese Weise funktionieren String-Literale und die meisten Operationen (mit Ausnahme der Zeicheniteration) komfortabel, ohne über die Unicode-Konvertierung nachzudenken, als gäbe es nur Python3. Datei-E / A benötigen natürlich immer besondere Sorgfalt bei der Codierung - wie in Python3.

Hinweis: Einfache Zeichenfolgen werden dann implizit von utf-8 in Unicode in SmartStdoutkonvertiert, bevor sie in den folgenden Ausgabestream konvertiert werden.

kxr
quelle
4

Hier ist der Ansatz, mit dem ich Code erstellt habe, der sowohl mit python2 als auch mit python3 kompatibel war und immer eine utf8- Ausgabe erzeugte . Ich habe diese Antwort woanders gefunden, kann mich aber nicht an die Quelle erinnern.

Dieser Ansatz ersetzt sys.stdoutdurch etwas, das nicht ganz dateiähnlich ist (aber immer noch nur Dinge in der Standardbibliothek verwendet). Dies kann durchaus Probleme für Ihre zugrunde liegenden Bibliotheken verursachen. In dem einfachen Fall, in dem Sie eine gute Kontrolle darüber haben, wie sys.stdout out über Ihr Framework verwendet wird, kann dies ein vernünftiger Ansatz sein.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Att Righ
quelle
3

Dies hat das Problem für mich behoben.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
twasbrillig
quelle
1

Dies ist ein schneller Hack für alle, die (1) auf einer Windows-Plattform (2) mit Python 2.7 und (3) verärgert sind, weil eine nette Software (dh nicht von Ihnen geschrieben, also nicht sofort ein Kandidat für das Codieren / Decodieren) ist Manöver) zeigen die "hübschen Unicode-Zeichen" in der IDLE-Umgebung nicht an (Pythonwin druckt Unicode-Fein), z. B. die sauberen Logiksymbole erster Ordnung, die Stephan Boyer in der Ausgabe seines pädagogischen Prüfers bei First Order Logic Prover verwendet .

Die Idee, ein sys-Reload zu erzwingen, gefiel mir nicht und ich konnte das System nicht dazu bringen, mit dem Festlegen von Umgebungsvariablen wie PYTHONIOENCODING zusammenzuarbeiten (versuchte es mit einer direkten Windows-Umgebungsvariablen und löschte diese auch in einer sitecustomize.py in Site-Paketen als Eins liner = 'utf-8').

Wenn Sie also bereit sind, Ihren Weg zum Erfolg zu hacken, wechseln Sie in Ihr IDLE-Verzeichnis. In der Regel: "C: \ Python27 \ Lib \ idlelib" Suchen Sie die Datei IOBinding.py. Erstellen Sie eine Kopie dieser Datei und speichern Sie sie an einem anderen Ort, damit Sie bei Bedarf zum ursprünglichen Verhalten zurückkehren können. Öffnen Sie die Datei in der Idlelib mit einem Editor (z. B. IDLE). Gehen Sie zu diesem Codebereich:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

Mit anderen Worten, kommentieren Sie die ursprüngliche Codezeile nach dem ' Versuch ' aus, bei dem die Codierungsvariable gleich locale.getdefaultlocale wurde (da Sie dadurch cp1252 erhalten, was Sie nicht möchten), und erzwingen Sie sie stattdessen brutal auf 'utf-8' '(durch Hinzufügen der Zeile' encoding = 'utf-8 ' wie gezeigt).

Ich glaube, dies wirkt sich nur auf die IDLE-Anzeige auf stdout aus und nicht auf die Codierung, die für Dateinamen usw. verwendet wird (die zuvor in der Dateisystemcodierung erhalten wurde). Wenn Sie ein Problem mit einem anderen Code haben, den Sie später in IDLE ausführen, ersetzen Sie einfach die Datei IOBinding.py durch die ursprüngliche unveränderte Datei.

Dalton Bentley
quelle
1

Sie können die Codierung Ihres gesamten Betriebssystems ändern. Unter Ubuntu können Sie dies mit tun

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Boris
quelle