2011-04-17 17 views
11

He aquí un pequeño programa:ayuda a entender por qué Unicode sólo funciona a veces con Python

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

print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 
print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 

En Ubuntu, terminal de Gnome, IPython no es lo que esperaría:

In [6]: run Unicodetest.py 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 

tengo la misma salida si ingreso los comandos en trypython.org.

codepad.org, por el contrario, produce un error para el segundo comando:

abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 
Traceback (most recent call last): 
    Line 6, in <module> 
    print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 
UnicodeEncodeError: 'ascii' codec can't encode character u'\u03a9' in position 6: ordinal not in range(128) 

Por el contrario, en reposo en Windows Mangles la salida del primer comando, pero no se queja sobre el segundo:

>>> 
abcd kΩ ☠°C √Hz µF ü ☃ ♥ 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 

IPython en un símbolo del sistema de Windows o a través de Python (x, y) 's versión Console2 tanto mangle la primera salida y se quejan de la segunda:

In [9]: run Unicodetest.py 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 
ERROR: An unexpected error occurred while tokenizing input 
The following traceback may be corrupted or invalid 
The error message is: ('EOF in multi-line statement', (15, 0)) 

--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 

Desktop\Unicodetest.py in <module>() 
     4 print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 
     5 
----> 6 print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 
     7 
     8 

C:\Python27\lib\encodings\cp437.pyc in encode(self, input, errors) 
    10 
    11  def encode(self,input,errors='strict'): 
---> 12   return codecs.charmap_encode(input,errors,encoding_map) 
    13 
    14  def decode(self,input,errors='strict'): 

UnicodeEncodeError: 'charmap' codec can't encode character u'\u2620' in position 8: character maps to <undefined> 
WARNING: Failure executing file: <Unicodetest.py> 

IPython dentro Python (x, y) 's Spyder hace lo mismo, pero de manera diferente:

In [8]: run Unicodetest.py 
abcd kΩ ☠°C √Hz µF ü ☃ ♥ 
------------------------------------------------------------ 
Traceback (most recent call last): 
    File "Unicodetest.py", line 6, in <module> 
    print(u'abcd kΩ ☠°C √Hz µF ü ☃ ♥') 
    File "C:\Python26\lib\encodings\cp1252.py", line 12, in encode 
    return codecs.charmap_encode(input,errors,encoding_table) 
UnicodeEncodeError: 'charmap' codec can't encode character u'\u03a9' in position 6: character maps to <undefined> 

WARNING: Failure executing file: <Unicodetest.py> 

(En sitecustomize.py, Spyder establece su propio SPYDER_ENCODING basado en la codificación del módulo de configuración regional, que es cp1252 para Windows 7.)

¿Qué ofrece? Es uno de mis comandos mal? ¿Por qué uno trabaja en algunas plataformas mientras que el otro trabaja en otras plataformas? ¿Cómo imprimo caracteres Unicode consistentemente sin colapsar o atornillar?

¿Hay un terminal alternativo para Windows que se comporte como el de Ubuntu? Parece que TCC-LE, Console2, Git Bash, PyCmd, etc. son solo envoltorios para cmd.exe en lugar de reemplazos. ¿Hay alguna forma de ejecutar IPython dentro de la interfaz que usa IDLE?

+2

En IPython, unicode lamentablemente está roto. Deberíamos tenerlo arreglado para la próxima versión, 0.11, por lo que se comporta como escribir en un intérprete de Python sin formato. –

+0

revise [this] (http://stackoverflow.com/q/39528462/5284370) fuera. – Soorena

Respuesta

10

E/S en Python (y en la mayoría de los demás idiomas) se basa en bytes. Cuando escribe una cadena de bytes (str en 2.x, bytes en 3.x) en un archivo, los bytes simplemente se escriben tal cual. Cuando escribe una cadena Unicode (unicode en 2.x, str en 3.x) en un archivo, los datos deben ser codificados en una secuencia de bytes.

Para una explicación más detallada de esta distinción, vea el Dive into Python 3 chapter on strings.

print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 

Aquí, la cadena es una cadena de bytes. Debido a que la codificación del archivo fuente es UTF-8, los bytes son

'abcd k\xce\xa9 \xe2\x98\xa0 \xc2\xb0C \xe2\x88\x9aHz \xc2\xb5F \xc3\xbc \xe2\x98\x83 \xe2\x99\xa5' 

La declaración print escribe estos bytes a la consola tal cual. Pero la consola de Windows interpreta las cadenas de bytes como codificadas en la página de códigos "OEM", que en los EE. UU. Es 437. Por lo que la cadena que realmente ve en su pantalla es

abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 

En su sistema Ubuntu, esto no causa un problema porque hay la codificación consola predeterminada es UTF-8, por lo que no tiene la discrepancia entre la fuente Codificación de archivos y codificación de consola.

print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 

Al imprimir una cadena Unicode, la cadena tiene que conseguir codificado en bytes. Pero solo funciona si tienes una codificación que admita esos caracteres. Y tu no.

  • El valor predeterminado IBM437 codificación carece de los caracteres ☠☃♥
  • El windows-1252 de codificación utilizado por Spyder carece de los caracteres Ω☠√☃♥.

Por lo tanto, en ambos casos, obtiene UnicodeEncodeError tratando de imprimir la cadena.

What gives?

Windows y Linux adoptaron enfoques muy diferentes para admitir Unicode.

Originalmente, ambos funcionaban más o menos de la misma manera: cada configuración regional tiene su propia codificación específica de idioma char (la "página de códigos ANSI" en Windows).Los idiomas occidentales usaban ISO-8859-1 o Windows-1252, ruso usaban KOI8-R o Windows-1251, etc.

Cuando Windows NT agregaba soporte para Unicode (en los primeros días cuando se suponía que Unicode usaría 16 caracteres de bit), lo hizo al crear una versión paralela de su API que usó wchar_t en lugar de char. Por ejemplo, la función MessageBox se dividió en las dos funciones:

int MessageBoxA(HWND hWnd, const char* lpText, const char* lpCaption, unsigned int uType); 
int MessageBoxW(HWND hWnd, const wchar_t* lpText, const wchar_t* lpCaption, unsigned int uType); 

Las funciones de "W" son los "verdaderos". Las funciones "A" existen para la compatibilidad con Windows basado en DOS y, en su mayoría, solo convierten sus argumentos de cadena en UTF-16 y luego llaman a la función "W" correspondiente.

En el mundo de Unix (concretamente, Plan 9), escribir una versión completamente nueva de la API POSIX no se consideraba práctico, por lo que el soporte Unicode se enfocó de manera diferente. El soporte existente para la codificación multibyte en las configuraciones regionales CJK se utilizó para implementar una nueva codificación ahora conocida como UTF-8.

La preferencia hacia UTF-8 en sistemas tipo Unix y UTF-16 en Windows es un gran dolor al escribir código multiplataforma que admite Unicode. Python intenta ocultar esto al programador, pero printing to the console es una de las "abstracciones de goteo" de Joel.

+0

Eso es muy útil, gracias. Todavía quiero saber si hay una manera de hacer que la "impresión" funcione en IPython en Windows, ya sea en la consola integrada de Windows o en alguna otra consola de terceros (si tal cosa existe). Si no es posible mostrar los caracteres especiales, al menos me gustaría imprimir "?" o algo sin chocar – endolith

+0

@christian: Sí, Notepad ++ puede guardarse en UTF-8, pero ese no parece ser el problema aquí. El problema es que la codificación del archivo no coincide con la codificación de stdout. – dan04

+0

Si un módulo está emitiendo una cadena como 'u'G \ xc3 \ xb6teborg, Sweden'', ¿no es esto incorrecto? Debería ser 'u'G \ xf6teborg, Sweden'' o, después de codificar para UTF-8,''G \ xc3 \ xb6teborg, Sweden'' sin 'u'. – endolith

0

Su problema aquí es que su programa espera, y las salidas, caracteres UTF-8, pero las consolas y varios corredores de Python en la web usan otras páginas de códigos. No hay forma de codificar caracteres especiales que funcionen en todas las codificaciones sin modificaciones. Sin embargo, si elige usar UTF-8 en todas partes, debe estar seguro.

Creo que cualquier terminal en Windows servirá, así que no se moleste en cambiar el predeterminado (cmd.exe) solo por esto. En su lugar, cambie también la codificación del terminal para que sea UTF-8, para que coincida con la codificación de su secuencia de comandos python.

Desafortunadamente, nunca he podido encontrar la manera de configurar la página de códigos en UTF-8 como predeterminada, por lo que debe hacerse cada vez que abra un nuevo símbolo del sistema. Pero se hace a través de un simple comando, por lo que es sólo la mitad-mal ... Se cambia la codificación por switching codepage:

>chcp 65001 
Current codepage is now 65001 

Tenga en cuenta que usted tiene que utilizar una de las fuentes estándar para que esto funcione. La mayoría de las fuentes en la web parecen sugerir la consola Lucida.

+0

Ahora cada comando que intento falla con 'LookupError: codificación desconocida: cp65001' debido a' line = raw_input_original (prompt) .decode (self.stdin_encoding) 'en' C: \ Python27 \ lib \ site-packages \ IPython \ iplib. pyc' – endolith

+3

Desafortunadamente, hay muchos problemas con 'chcp 65001'. El tiempo de ejecución de Microsoft C y la consola predeterminada de Windows están diseñados para funcionar con páginas de códigos específicos de la configuración regional; cuando todos los demás se están moviendo a UTF-8-para-todo esto es una verdadera lástima. – bobince

2

Hay dos razones posibles:

  • de Unicode por print. No se puede generar Unicode sin procesar, por lo que print necesita encontrar la forma de convertirlo a la secuencia de bytes que espera la consola (usa sys.stdout.encoding AFAIK), lo que nos lleva al
  • Soporte de la consola. Python no controla tu terminal, por lo que si escupe UTF-8 mientras tu terminal espera algo más, obtendrás resultados destrozados.
0

La salida Unicode de Python a la consola de Windows simplemente no funciona. No se puede persuadir a Python de que emita la codificación nativa de Windows que espera caracteres anchos y UCS2.

+2

Estoy encantado de haber votado aquí porque significa que estoy equivocado y finalmente podré obtener una buena compatibilidad con Unicode en una consola de Windows. Ahora solo estoy esperando los detalles de cómo hacer eso. –

+1

Bueno ... ni siquiera puede 'simplemente dar salida a UCS-2' con el tiempo de ejecución C estándar, siempre usa una página de códigos ASCII-superconjunto específica de la configuración regional (nunca un UTF de ningún tipo). Existe una interfaz específica de Win32 separada que se puede usar para generar contenido Unicode, 'WriteConsoleW', pero luego debe decidir si la salida de bytes o caracteres es lo que pretende hacer, lo que podría depender de la plataforma, o si su IO las secuencias se redireccionan al archivo. Es todo un desastre, esto. – bobince

+0

@bobince resulta que es un mito expuesto por Michael Kaplan: http://blogs.msdn.com/b/michkap/archive/2008/03/18/8306597.aspx ¡Canta ho para '_O_U16TEXT'! –

0

@ dan04: Tiene razón en que el problema es que la codificación del archivo no coincide con la codificación de stdout. Sin embargo, una forma de resolver el problema es cambiar la codificación del archivo. Entonces, en Windows Notepad ++ se puede usar para guardar el código con codificación de caracteres UTF-8.

Una alternativa es la recodificación GNU.

Cuestiones relacionadas