type caracteres python windows unicode encoding cmd

caracteres - unicode en python 3



El cambio de codificación de cmd de Windows provoca la falla de Python (8)

¿Quieres que Python codifique para UTF-8?

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

Python no reconocerá cp65001 como UTF-8.

Primero cambio la codificación de Windows CMD a utf-8 y ejecuto el intérprete de Python:

chcp 65001 python

Luego trato de imprimir una picadura Unicode dentro y cuando lo hago, Python se cuelga 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 devuelve ''cp65001''

UPD2 : Me vino a la mente que el problema podría estar relacionado con el hecho de que utf-8 usa un juego de caracteres de múltiples bytes (kcwu hizo un buen punto al respecto). 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 error conocido de Python . 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.


A continuación, se cp65001 cómo alias cp65001 a UTF-8 sin cambiar las encodings/aliases.py :

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

(En mi humilde opinión, no preste atención a las tonterías acerca de que cp65001 no sea idéntico a UTF-8 en http://bugs.python.org/issue6058#msg97731 . Está destinado a ser el mismo, incluso si el códec de Microsoft tiene algún inconveniente loco.)

Aquí hay un código (escrito para Tahoe-LAFS, tahoe-lafs.org) que hace que la salida de la consola funcione independientemente de la página de códigos chcp , y también lee los argumentos de la línea de comandos Unicode. Gracias 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 utiliza WriteConsoleW lugar de la bandera _O_U8TEXT en la biblioteca de tiempo de ejecución de _O_U8TEXT , que tiene errores. WriteConsoleW también tiene errores en relación con la documentación de MS, pero menos).

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://.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 otorgarle a ΤΖΩΤΖΙΟΥ el deseo de utilizar DejaVu Sans Mono, que acepto que es una excelente fuente para la consola.

Puede encontrar información sobre los requisitos de fuentes y cómo agregar nuevas fuentes para la consola de Windows en ''Criterios necesarios para que las fuentes estén disponibles en una ventana de comandos'' Microsoft KB

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

  • en HKEY_LOCAL_MACHINE_SOFTWARE/Microsoft/Windows NT/CurrentVersion/Console/TrueTypeFont , establezca "0" en "DejaVu Sans Mono" ;
  • para cada una de las subclaves en HKEY_CURRENT_USER/Console , configure "FaceName" en "DejaVu Sans Mono" .

En XP, verifique el hilo ''¿Cómo cambiar las fuentes del símbolo del sistema?'' en los foros de LockerGnome .


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 conocida para python. Pruebe con ''UTF-16'' o algo similar.


Establecer la variable del sistema PYTHONIOENCODING :

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

La fuente de example.py es simple:

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


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


Para codificación desconocida: problema cp65001, puede establecer una nueva variable como PYTHONIOENCODING y un valor como UTF-8. (Esto funciona para mí)

Ver esto


Para mí configurar este env var antes de la ejecución del programa python funcionó:

set PYTHONIOENCODING=utf-8


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

Tome esta secuencia de comandos (por ejemplo, uniconsole.py en su sitio-paquetes 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 funcionar en torno a la falla de Python (o error de la consola win32 Unicode, lo que sea). Luego agregué en todos los scripts relacionados:

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

Finalmente, solo ejecuto mis scripts según sea necesario en una consola donde se ejecuta chcp 65001 y la fuente es Lucida Console . (Cómo desearía que DejaVu Sans Mono pudiera usar en su lugar ... pero piratear el registro y seleccionarlo como una fuente de consola revierte a una fuente de mapa de bits).

Este es un reemplazo de stdout y stderr rápido y sucio, y tampoco maneja ningún raw_input relacionado con raw_input (obviamente, ya que no toca sys.stdin en absoluto). Y, dicho sea de paso, agregué el alias utf_8 para utf_8 en el archivo encodings/aliases.py de la lib estándar.