2009-05-18 6 views
50

Zuerst habe ich Windows-CMD-Kodierung UTF-8 ändern und Python-Interpreter ausführen:Windows-cmd-Codierung Änderung bewirkt, dass Python Absturz

chcp 65001 
python 

Dann versuche ich in ihm einen Unicode-Stachel zu drucken und wenn ich dies tun Python stürzt in eine besondere Art und Weise (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 kehrt 'cp65001'

UPD2: Mir kam gerade, dass das Problem könnte mit der Tatsache zusammenhängen, dass utf-8 verwendet multi-byte character set (kcwu einen guten Punkt auf das machte). Ich habe versucht das ganze Beispiel mit 'windows-1250' zu laufen und bekam ''aa?' '. Windows-1250 verwendet einen einzelnen Zeichensatz, so dass er für die Zeichen funktioniert, die er versteht. Ich habe aber immer noch keine Idee, wie man 'utf-8' hier arbeiten kann.

UPD3: Oh, ich fand heraus, es ist ein known Python bug. Ich nehme an, dass Python die cmd-Codierung als 'cp65001' in 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.

+0

können Sie drucken sys.stdin.encoding? Was gibt es zurück? – nosklo

+0

Ich gebe 'cp65001' zurück – Alex

+5

Es ist einfach für Python zu wissen, wie man mit dem Codec 'cp65001' umgehen kann: man muss eine Zeile zu Lib/encodings/aliases.py hinzufügen, Zuordnung 'cp65001' zu 'utf_8'. Ich habe dafür einen Patch erstellt und auch den von dir erwähnten Fehler Alex aktualisiert. Es gibt jedoch immer noch Probleme. – tzot

Antwort

2

Dies liegt daran, „Codepage“ von cmd anders zu „MBCS“ des Systems. Obwohl Sie die "Code-Seite" geändert haben, glauben Python (eigentlich Windows) immer noch, dass sich Ihr "mbcs" nicht ändert.

1

Haben Sie Python auf UTF-8 kodieren möchten?

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

Python erkennt cp65001 nicht als UTF-8.

+0

Das ist eine gute einfache Lösung für schnelles Debuggen. –

1

Ein paar Anmerkungen: Sie falsch geschrieben wahrscheinlich encodig und .code. 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 Codierung für Python. Versuchen Sie 'UTF-16' oder etwas Ähnliches.

+0

Ja, ich habe es auf jeden Fall falsch geschrieben, aber ich habe es auf die richtige Art und Weise ausprobiert (das beweist tatsächlich, dass der Interpreter die falsch geschriebenen 'encode()' und 'encoding()') Attribute nicht ausgewertet hat und dabei abgestürzt ist Ich habe den Tippfehler behoben. – Alex

3

Ich hatte dieses lästige Problem, auch, und ich hasste meine Unicode-fähigen Skripte gleiche in MS Windows nicht wie unter Linux in der Lage zu laufen. Also habe ich mir einen Workaround ausgedacht.

Nehmen Sie dieses Skript (etwa uniconsole.py in Ihrer Website-Pakete 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 sich um den Python Fehler zu arbeiten (oder win32 Unicode-Konsole Fehler, was auch immer). Dann habe ich in allen verwandten Skripte:

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

Schließlich habe ich nur meine Skripte laufen wie in einer Konsole benötigt, wo chcp 65001 ausgeführt wird und die Schrift ist Lucida Console. (Wie ich wünsche, dass DejaVu Sans Mono stattdessen verwendet werden könnte ... aber das Hacken der Registrierung und Auswählen als eine Konsolenschriftart wird zu einer Bitmap-Schriftart zurückgenommen.)

Dies ist ein schneller und unsaubere stdout und stderr Ersatz, und auch alle raw_input ähnlichen Fehler nicht behandeln ist (natürlich, da es nicht sys.stdin überhaupt nicht berührt). Und übrigens, ich habe den cp65001 Alias ​​für utf_8 in der encodings\aliases.py Datei der Standardbibliothek hinzugefügt.

+0

Dies funktioniert perfekt! Fügen Sie auch mindestens eine leere 'def flush (self): pass 'zur Klasse hinzu, damit sie mit' stderr'/'stdout' kompatibel ist (möglicherweise mehr Methoden fehlen, aber Twisted beschwerte sich nur über '.flush()' fehlt). –

+0

Nach der Verwendung Ihres Schnipsel, sieht aus wie David-Sarah Hopwood Schnipsel funktioniert universeller. –

+0

_DebuggerOutput hat kein Attribut fileno – isarandi

76

Hier ist, wie cp65001 auf UTF-8 an Alias ​​ohne Änderung encodings\aliases.py:

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

(IMHO, zahlen keine Aufmerksamkeit auf die Albernheit über cp65001 nicht identisch mit UTF-8 ist in http://bugs.python.org/issue6058#msg97731 Es ist beabsichtigt ist. die gleiche, auch wenn Microsoft-Codec hat einige kleinere Fehler zu sein.)

Hier einige Code ist (geschrieben für Tahoe-LAFS, tahoe-lafs.org), die Ausgabe der Konsole arbeiten unabhängig der chcp Codepage macht, und liest auch Unicode-Befehlszeilenargumente Kredit an Michael Kaplan für die Idee hinter dieser Lösung. Wenn stdout oder stderr umgeleitet werden, wird UTF-8 ausgegeben. Wenn Sie eine Byte Order Mark benötigen, müssen Sie sie explizit schreiben.

[Bearbeiten: Diese Version verwendet WriteConsoleW anstelle der _O_U8TEXT Flag in der MSVC-Laufzeitbibliothek, die fehlerhaft ist. WriteConsoleW ist auch ein Fehler in Bezug auf die MS-Dokumentation, 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 möglich ist ΤΖΩΤΖΙΟΥ Wunsch zu verwenden DejaVu Sans Mono, die ich für die Konsole eine hervorragende Schrift, stimmen zu gewähren .

Sie können Informationen über die Schriftart Anforderungen finden und wie in den 'Necessary criteria for fonts to be available in a command window' Microsoft KB neue Schriftarten für die Windows-Konsole hinzuzufügen

Aber im Grunde auf Vista (wahrscheinlich auch Win7):

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

Unter XP, überprüfen Sie den Thread 'Changing Command Prompt fonts?' in LockerGnome forums.

+3

+1, weil Ihre Antwort ist würdig, plus ein virtuelles +1 für den Font-Vorschlag, auch wenn es zu spät ist (ich und Windows hatten eine Trennung mit vielen Kämpfen; ich glaube nicht, dass wir jemals wieder zusammen sein werden, aber dafür kurze Begegnungen bei Freunden Computer :) Danke. – tzot

+2

@ David-Sarah: Danke für den sehr nützlichen Code! Weißt du zufällig, ob es eine entsprechende Möglichkeit gibt, console * input * zu fixieren (z. B. Unicode-Zeichen kopieren, einfach einfügen, unabhängig von Codepage usw.). Dies würde vermutlich ReadConsoleW beinhalten? –

+0

Das ist möglich, und in der Tat würde es ReadConsoleW verwenden. Ursprünglich wollte ich diesen Code schreiben, aber ich benutze Windows seit einiger Zeit nicht mehr. Wenn Sie sich für Python 3 interessieren, ist der entsprechende Fehler http://bugs.python.org/issue1602, obwohl es noch keine Lösung für die Eingabe gibt. (Ein Patch für diesen Bug würde von Python 3 Interna abhängen und wäre nicht leicht an Python 2.x anpassbar.) –

36

Set PYTHONIOENCODING Systemvariable:

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

Quelle von example.py ist einfach:

import sys 
print "Encoding is", sys.stdin.encoding 
+3

Und vergessen Sie nicht, die richtige Schriftart einzustellen. – DenisKolodin

+2

Ich habe das in Python 2.7.5 versucht, und während 'sys.stdin.encoding' und' sys.stdout.encoding' beide sagten 'utf-8', erzeugte es nicht die richtige Ausgabe. Es zeigte jedes Byte der Ausgabe als einzelne Zeichen, anstatt sie in Codepunkte zu kombinieren. –

+0

'python -c" import sys; print ('Encoding =' + sys.stdin.encoding) "' anstatt eine Datei zu erstellen. –

0

Für unbekannte Codierung: cp65001 Ausgabe, können neue Variable als PYTHONIOENCODING gesetzt und Wert als UTF-8. (Dies funktioniert für mich)

View this

Verwandte Themen