it-swarm.com.de

Die Änderung der Windows-Cmd-Codierung führt zum Absturz von Python

Zuerst ändere ich die Windows-CMD-Codierung in utf-8 und führe den Python-Interpreter aus:

chcp 65001
python

Dann versuche ich, einen Unicode-Stachel darin zu drucken, und Python stürzt dann auf eine eigenartige Weise ab (ich bekomme nur eine cmd-Eingabeaufforderung im selben Fenster). 

>>> import sys
>>> print u'ëèæîð'.encode(sys.stdin.encoding)

Irgendwelche Ideen, warum es passiert und wie es funktioniert?

UPD: sys.stdin.encoding gibt 'cp65001' zurück

UPD2Ich kam gerade zu mir, dass das Problem damit zusammenhängen könnte, dass utf-8 Mehrbyte-Zeichensatz verwendet (kcwu hat dies sehr deutlich gemacht). Ich habe versucht, das ganze Beispiel mit 'Windows-1250' auszuführen und bekam '' A '?' Windows-1250 verwendet einen Zeichensatz, so dass es für die Zeichen funktioniert, die es versteht. Ich habe jedoch immer noch keine Ahnung, wie "utf-8" hier funktionieren soll.

UPD3: Oh, ich habe herausgefunden, dass es sich um einen bekannten Python-Bug handelt. Ich denke, was passiert, ist, dass Python die cmd-Kodierung als 'cp65001 nach sys.stdin.encoding kopiert und versucht, sie auf alle Eingaben anzuwenden. Da es 'cp65001' nicht versteht, stürzt es bei jeder Eingabe ab, die Nicht-ASCII-Zeichen enthält.

55
Alex

So aliasen Sie cp65001 auf UTF-8, ohne encodings\aliases.py zu ändern:

import codecs
codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)

(IMHO, achten Sie nicht darauf, dass cp65001 nicht mit UTF-8 unter http://bugs.python.org/issue6058#msg97731 identisch ist das Gleiche sein, auch wenn der Codec von Microsoft einige kleinere Fehler aufweist.)

Hier ist ein Code (geschrieben für Tahoe-LAFS, tahoe-lafs.org), mit dem die Konsolenausgabe funktioniert nabhängig von der Codepage chcp und auch Unicode-Befehlszeilenargumente gelesen werden. Dank an Michael Kaplan für die Idee hinter dieser Lösung. Wenn stdout oder stderr umgeleitet werden, wird UTF-8 ausgegeben. Wenn Sie ein Byte Order Mark wünschen, müssen Sie es explizit schreiben.

[Bearbeiten: Diese Version verwendet WriteConsoleW anstelle des Flags _O_U8TEXT in der MSVC-Laufzeitbibliothek, die fehlerhaft ist. WriteConsoleW ist auch in Bezug auf die MS-Dokumentation fehlerhaft, aber weniger.]

import sys
if sys.platform == "win32":
    import codecs
    from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int
    from ctypes.wintypes import BOOL, HANDLE, DWORD, LPWSTR, LPCWSTR, LPVOID

    original_stderr = sys.stderr

    # If any exception occurs in this code, we'll probably try to print it on stderr,
    # which makes for frustrating debugging if stderr is directed to our wrapper.
    # So be paranoid about catching errors and reporting them to original_stderr,
    # so that we can at least see them.
    def _complain(message):
        print >>original_stderr, message if isinstance(message, str) else repr(message)

    # Work around <http://bugs.python.org/issue6058>.
    codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)

    # Make Unicode console output work independently of the current code page.
    # This also fixes <http://bugs.python.org/issue1602>.
    # Credit to Michael Kaplan <http://www.siao2.com/2010/04/07/9989346.aspx>
    # and TZOmegaTZIOY
    # <http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash/1432462#1432462>.
    try:
        # <http://msdn.Microsoft.com/en-us/library/ms683231(VS.85).aspx>
        # HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
        # returns INVALID_HANDLE_VALUE, NULL, or a valid handle
        #
        # <http://msdn.Microsoft.com/en-us/library/aa364960(VS.85).aspx>
        # DWORD WINAPI GetFileType(DWORD hFile);
        #
        # <http://msdn.Microsoft.com/en-us/library/ms683167(VS.85).aspx>
        # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode);

        GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32))
        STD_OUTPUT_HANDLE = DWORD(-11)
        STD_ERROR_HANDLE = DWORD(-12)
        GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32))
        FILE_TYPE_CHAR = 0x0002
        FILE_TYPE_REMOTE = 0x8000
        GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32))
        INVALID_HANDLE_VALUE = DWORD(-1).value

        def not_a_console(handle):
            if handle == INVALID_HANDLE_VALUE or handle is None:
                return True
            return ((GetFileType(handle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR
                    or GetConsoleMode(handle, byref(DWORD())) == 0)

        old_stdout_fileno = None
        old_stderr_fileno = None
        if hasattr(sys.stdout, 'fileno'):
            old_stdout_fileno = sys.stdout.fileno()
        if hasattr(sys.stderr, 'fileno'):
            old_stderr_fileno = sys.stderr.fileno()

        STDOUT_FILENO = 1
        STDERR_FILENO = 2
        real_stdout = (old_stdout_fileno == STDOUT_FILENO)
        real_stderr = (old_stderr_fileno == STDERR_FILENO)

        if real_stdout:
            hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
            if not_a_console(hStdout):
                real_stdout = False

        if real_stderr:
            hStderr = GetStdHandle(STD_ERROR_HANDLE)
            if not_a_console(hStderr):
                real_stderr = False

        if real_stdout or real_stderr:
            # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
            #                           LPDWORD lpCharsWritten, LPVOID lpReserved);

            WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID)(("WriteConsoleW", windll.kernel32))

            class UnicodeOutput:
                def __init__(self, hConsole, stream, fileno, name):
                    self._hConsole = hConsole
                    self._stream = stream
                    self._fileno = fileno
                    self.closed = False
                    self.softspace = False
                    self.mode = 'w'
                    self.encoding = 'utf-8'
                    self.name = name
                    self.flush()

                def isatty(self):
                    return False

                def close(self):
                    # don't really close the handle, that would only cause problems
                    self.closed = True

                def fileno(self):
                    return self._fileno

                def flush(self):
                    if self._hConsole is None:
                        try:
                            self._stream.flush()
                        except Exception as e:
                            _complain("%s.flush: %r from %r" % (self.name, e, self._stream))
                            raise

                def write(self, text):
                    try:
                        if self._hConsole is None:
                            if isinstance(text, unicode):
                                text = text.encode('utf-8')
                            self._stream.write(text)
                        else:
                            if not isinstance(text, unicode):
                                text = str(text).decode('utf-8')
                            remaining = len(text)
                            while remaining:
                                n = DWORD(0)
                                # There is a shorter-than-documented limitation on the
                                # length of the string passed to WriteConsoleW (see
                                # <http://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232>.
                                retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None)
                                if retval == 0 or n.value == 0:
                                    raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value))
                                remaining -= n.value
                                if not remaining:
                                    break
                                text = text[n.value:]
                    except Exception as e:
                        _complain("%s.write: %r" % (self.name, e))
                        raise

                def writelines(self, lines):
                    try:
                        for line in lines:
                            self.write(line)
                    except Exception as e:
                        _complain("%s.writelines: %r" % (self.name, e))
                        raise

            if real_stdout:
                sys.stdout = UnicodeOutput(hStdout, None, STDOUT_FILENO, '<Unicode console stdout>')
            else:
                sys.stdout = UnicodeOutput(None, sys.stdout, old_stdout_fileno, '<Unicode redirected stdout>')

            if real_stderr:
                sys.stderr = UnicodeOutput(hStderr, None, STDERR_FILENO, '<Unicode console stderr>')
            else:
                sys.stderr = UnicodeOutput(None, sys.stderr, old_stderr_fileno, '<Unicode redirected stderr>')
    except Exception as e:
        _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))


    # While we're at it, let's unmangle the command-line arguments:

    # This works around <http://bugs.python.org/issue2128>.
    GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
    CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(("CommandLineToArgvW", windll.Shell32))

    argc = c_int(0)
    argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))

    argv = [argv_unicode[i].encode('utf-8') for i in xrange(0, argc.value)]

    if not hasattr(sys, 'frozen'):
        # If this is an executable produced by py2exe or bbfreeze, then it will
        # have been invoked directly. Otherwise, unicode_argv[0] is the Python
        # interpreter, so skip that.
        argv = argv[1:]

        # Also skip option arguments to the Python interpreter.
        while len(argv) > 0:
            arg = argv[0]
            if not arg.startswith(u"-") or arg == u"-":
                break
            argv = argv[1:]
            if arg == u'-m':
                # sys.argv[0] should really be the absolute path of the module source,
                # but never mind
                break
            if arg == u'-c':
                argv[0] = u'-c'
                break

    # if you like:
    sys.argv = argv

Schließlich ist es ist ​​möglich, ΤΖΩΤΖΙΟΥs Wunsch zu erfüllen, DejaVu Sans Mono für die Konsole zu verwenden, was meiner Meinung nach eine hervorragende Schriftart ist.

Informationen zu den Schriftartanforderungen und zum Hinzufügen neuer Schriftarten für die Windows-Konsole finden Sie unter 'Erforderliche Kriterien für die Verfügbarkeit von Schriftarten in einem Befehlsfenster', Microsoft KB

Aber im Grunde genommen unter Vista (wahrscheinlich auch Win7):

  • setzen Sie unter HKEY_LOCAL_MACHINE_SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont"0" auf "DejaVu Sans Mono";
  • setzen Sie für jeden der Unterschlüssel unter HKEY_CURRENT_USER\Console"FaceName" auf "DejaVu Sans Mono".

Überprüfen Sie unter XP den Thread 'Ändern von Eingabeaufforderungsschriftarten?' In LockerGnome-Foren .

80
Daira Hopwood

SetzePYTHONIOENCODINGSystemvariable:

> chcp 65001
> set PYTHONIOENCODING=utf-8
> python example.py
Encoding is utf-8

Die Quelle von example.py ist einfach:

import sys
print "Encoding is", sys.stdin.encoding
42
DenisKolodin

Ich hatte auch dieses ärgerliche Problem und ich hasste es, meine Unicode-fähigen Skripte nicht in der gleichen Weise wie in Linux ausführen zu können. So gelang es mir, eine Problemumgehung zu finden.

Nehmen Sie dieses Skript (sagen Sie uniconsole.py in Ihren Site-Paketen oder was auch immer):

import sys, os

if sys.platform == "win32":
    class UniStream(object):
        __slots__= ("fileno", "softspace",)

        def __init__(self, fileobject):
            self.fileno = fileobject.fileno()
            self.softspace = False

        def write(self, text):
            os.write(self.fileno, text.encode("utf_8") if isinstance(text, unicode) else text)

    sys.stdout = UniStream(sys.stdout)
    sys.stderr = UniStream(sys.stderr)

Dies scheint den Python-Fehler zu umgehen (oder den Win32-Unicode-Konsolen-Fehler). Dann fügte ich in allen verwandten Skripten hinzu:

try:
    import uniconsole
except ImportError:
    sys.exc_clear()  # could be just pass, of course
else:
    del uniconsole  # reduce pollution, not needed anymore

Zum Schluss führe ich einfach meine Skripts nach Bedarf in einer Konsole aus, in der chcp 65001 ausgeführt wird und die Schriftart Lucida Console ist. (Ich wünsche mir, dass stattdessen DejaVu Sans Mono verwendet werden könnte. Wenn Sie jedoch die Registrierung hacken und als Konsolenschriftart auswählen, wird die Bitmap-Schriftart wiederhergestellt.)

Dies ist eine schnelle und schmutzige stdout- und stderr-Ersetzung und behandelt auch keine raw_input-bezogenen Fehler (offensichtlich, da sie sys.stdin überhaupt nicht berühren). Ich habe übrigens den cp65001-Alias ​​für utf_8 in der encodings\aliases.py-Datei der Standard-Bibliothek hinzugefügt.

3
tzot

Möchten Sie, dass Python nach UTF-8 kodiert?

>>>print u'ëèæîð'.encode('utf-8')
ëèæîð

Python erkennt cp65001 nicht als UTF-8. 

3
Jason Coon

Dies liegt daran, dass "Codepage" von cmd sich von "mbcs" des Systems unterscheidet. Obwohl Sie die "Codepage" geändert haben, glauben Python (eigentlich Fenster) immer noch, dass sich Ihre "mbcs" nicht ändern.

2
kcwu

Ein paar Kommentare: Sie haben wahrscheinlich encodig und .code falsch geschrieben. Hier ist mein Beispiel.

C:\>chcp 65001
Active code page: 65001

C:\>\python25\python
...
>>> import sys
>>> sys.stdin.encoding
'cp65001'
>>> s=u'\u0065\u0066'
>>> s
u'ef'
>>> s.encode(sys.stdin.encoding)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
LookupError: unknown encoding: cp65001
>>>

Die Schlussfolgerung - cp65001 ist keine bekannte Kodierung für Python. Versuchen Sie 'UTF-16' oder etwas Ähnliches.

1
gimel

Bei unbekannter Codierung: cp65001 kann die neue Variable als PYTHONIOENCODING und Value als UTF-8 festgelegt werden. (Das funktioniert für mich)

Ansehen:
 View this

1
WenJun

Für mich, wenn Sie diese Env-Variable einstellen, bevor die Ausführung des Python-Programms funktioniert:

set PYTHONIOENCODING=utf-8
1
damian1baran

Das Problem wurde in diesem Thread gelöst und behandelt:

Ändern Sie die Systemcodierung

Die Lösung besteht darin, Unicode UTF-8 für die weltweite Unterstützung in Win abzuwählen. Es ist ein Neustart erforderlich, nach dem sich Ihr Python wieder normalisiert. 

Schritte für Win:

  1. Gehen Sie zur Systemsteuerung
  2. Wählen Sie Uhr und Region
  3. Klicken Sie auf Region> Administrative
  4. Klicken Sie unter Sprache für Nicht-Unicode-Programme auf "Systemgebietsschema ändern".
  5. Deaktivieren Sie im eingeblendeten Fenster "Region Settings" die Option "Beta: Use Unicode UTF-8 ...".
  6. Starten Sie den Computer gemäß der Windows-Eingabeaufforderung neu

Das Bild zeigt den genauen Ort, um das Problem zu lösen:

So lösen Sie das Problem

0
V C