2009-05-18 26 views
50

Primero cambiar de Windows CMD codificación UTF-8 y ejecutar Python intérprete:de Windows cambio cmd causas de codificación accidente Python

chcp 65001 
python 

entonces trato de imprimir una picadura de Unicode en su interior y cuando hago esto accidentes Python en de una manera peculiar (obtengo un prompt de cmd en la misma ventana).

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

¿Alguna idea de por qué sucede y cómo hacer que funcione?

UPD: sys.stdin.encoding vuelve 'cp65001'

UPD2: Se me acaba de ocurrir que el problema podría estar relacionado con el hecho de que UTF-8 utiliza multi-byte character set (KCWU hizo un buen punto en eso). Intenté ejecutar todo el ejemplo con 'windows-1250' y obtuve 'ëeaî'? '. Windows-1250 usa un conjunto de caracteres únicos, por lo que funcionó para los personajes que comprende. Sin embargo, todavía no tengo idea de cómo hacer que 'utf-8' funcione aquí.

UPD3: Oh, descubrí que es un known Python bug. Supongo que lo que sucede es que Python copia la codificación cmd como 'cp65001 en sys.stdin.encoding e intenta aplicarla a toda la entrada. Como no comprende 'cp65001', falla en cualquier entrada que contenga caracteres que no sean ascii.

+0

puede imprimir sys.stdin.encoding? ¿Qué devuelve? – nosklo

+0

Devuelvo 'cp65001' – Alex

+5

Es fácil para Python saber cómo lidiar con el códec 'cp65001': hay que agregar una línea a Lib/encodings/aliases.py, mapeando 'cp65001' a 'utf_8'. Creé un parche para eso y también actualicé el error que mencionas, Alex. Todavía hay problemas, sin embargo. – tzot

Respuesta

2

Esto se debe a que la "página de códigos" de cmd es diferente de "mbcs" del sistema. Aunque cambió la "página de códigos", Python (en realidad, Windows) aún piensa que sus "mbcs" no cambian.

1

¿Desea que Python codifique para UTF-8?

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

Python no reconocerá cp65001 como UTF-8.

+0

Esa es una buena solución simple para la depuración rápida. –

1

Algunos comentarios: es probable que haya escrito mal encodig y .code. Aquí está mi carrera de tu ejemplo.

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 
>>> 

La conclusión - cp65001 no es una codificación conocido por pitón. Pruebe con 'UTF-16' o algo similar.

+0

Sí, definitivamente lo escribí incorrectamente, pero lo intenté de la manera correcta y con el mismo bloqueo (esto prueba que el intérprete no pudo evaluar los atributos 'encode()' y 'encoding()' mal escritos y se bloqueó mientras procesando 'ëèæîð'. Reparé el error tipográfico – Alex

3

También tuve este problema molesto, y odiaba no poder ejecutar mis scripts con reconocimiento de Unicodo en MS Windows como en Linux. Entonces, logré encontrar una solución alternativa.

Tome este script (por ejemplo, uniconsole.py en su site-packages o lo que sea):

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) 

Esto parece evitar el error de Python (o Win32 Unicode fallo de la consola, lo que sea). Luego añade en todas las secuencias de comandos relacionados:

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

Por último, acabo de correr mis guiones, según sea necesario en una consola donde chcp 65001 se ejecuta y el tipo de letra es Lucida Console. (Cómo desearía que DejaVu Sans Mono se pudiera usar en su lugar ... pero piratear el registro y seleccionarlo como una fuente de consola revierte a una fuente de mapa de bits.)

Ésta es una stdout y stderr reemplazo rápido y sucio-y también no maneja ningún raw_input errores relacionados (obviamente, ya que no toca sys.stdin en absoluto). Y, por cierto, he agregado el alias cp65001 para utf_8 en el archivo encodings\aliases.py de la lib estándar.

+0

¡Esto funciona perfectamente! Además, agregue al menos un 'def flush (self): pass' vacío a la clase para que sea compatible con' stderr'/'stdout' (posiblemente faltan más métodos, pero Twisted solo se quejó de '.flush()' faltante). –

+0

Después de haber usado su fragmento, parece que el fragmento de David-Sarah Hopwood funciona de manera más universal. –

+0

_DebuggerOutput no tiene ningún atributo fileno – isarandi

76

Así es como poner un alias cp65001 a UTF-8 sin cambiar encodings\aliases.py:

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

(en mi humilde opinión, no prestar ninguna atención a la tontería sobre cp65001 no ser idéntica a UTF-8 en http://bugs.python.org/issue6058#msg97731 Está pensado. siendo la misma, incluso si el códec de Microsoft tiene algunos errores menores.)

Aquí hay un código (escrito para Tahoe-SIAL, tahoe-lafs.org) que hace funcionar salida de la consola independientemente de la página chcp código, y también lee Argumentos de línea de comandos Unicode. Crédito a Michael Kaplan por la idea detrás de esta solución. Si se redirigen stdout o stderr, se generará UTF-8. Si desea una Marca de orden de bytes, deberá escribirla explícitamente.

[Editar: Esta versión usa WriteConsoleW en lugar de la bandera _O_U8TEXT en la biblioteca de tiempo de ejecución MSVC, que tiene errores. WriteConsoleW es también con errores en relación con la documentación de MS, pero en menor medida.]

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 

Finalmente, es posible conceder el deseo de ΤΖΩΤΖΙΟΥ utilizar DejaVu Sans Mono, que estoy de acuerdo es una excelente fuente, para la consola .

puede encontrar información sobre los requisitos de la fuente y cómo añadir nuevas fuentes para la consola de ventanas en el 'Necessary criteria for fonts to be available in a command window' Microsoft KB

Pero, básicamente, en Vista (probablemente también Win7):

  • bajo HKEY_LOCAL_MACHINE_SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont, establecer "0" a "DejaVu Sans Mono";
  • para cada una de las subclaves bajo HKEY_CURRENT_USER\Console, establecer "FaceName" en "DejaVu Sans Mono".

En XP, compruebe el hilo 'Changing Command Prompt fonts?' in LockerGnome forums.

+3

+1 porque su respuesta es digno, más un +1 virtual para la sugerencia de la fuente, aunque es demasiado tarde (yo y Windows hemos tenido una ruptura con muchas peleas, no creo que volveremos a estar juntos de nuevo, excepto por encuentros breves en las computadoras de los amigos :) Gracias. – tzot

+2

@ David-Sarah: ¡Gracias por el código tan útil! ¿Sabe por casualidad si existe una forma correspondiente de corregir la entrada * de la consola (de modo que, por ejemplo, los caracteres unicode copiados y pegados solo funcionan, independientemente de la página de códigos, etc.), esto supondría probablemente que ReadConsoleW? –

+0

Eso es posible, y de hecho usaría ReadConsoleW. Originalmente iba a escribir ese código, pero hace tiempo que no uso Windows. Si su interés está en Python 3, el error relevante es http://bugs.python.org/issue1602, aunque todavía no tiene una solución para la entrada. (Un parche para ese error dependería de las partes internas de Python 3 y no sería fácilmente adaptable a Python 2.x.) –

36

Establecer PYTHONIOENCODING variable de sistema:

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

Fuente de example.py es simple:

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

Y no te olvides de configurar la fuente correcta. – DenisKolodin

+2

Intenté esto en Python 2.7.5, y mientras 'sys.stdin.encoding' y' sys.stdout.encoding' ambos dijeron 'utf-8' no generó el resultado correcto. Mostraba cada byte de salida como caracteres individuales en lugar de combinarlos en puntos de código. –

+0

'python -c" import sys; print ('Encoding =' + sys.stdin.encoding) '' en lugar de hacer un archivo. –

0

Para la codificación desconocida: tema cp65001, puede fijar nueva variable como PYTHONIOENCODING y Valor como UTF-8. (Esto funciona para mí)

View this

Cuestiones relacionadas