Pregunta

Del caparazón de Python 2.6:

>>> import sys
>>> print sys.getdefaultencoding()
ascii
>>> print u'\xe9'
é
>>> 

Esperaba tener algo de galimatías o un error después de la declaración de impresión, ya que el carácter "é" no es parte de ASCII y no he especificado una codificación. Supongo que no entiendo qué significa ASCII la codificación predeterminada.

EDITAR

Moví la edición al Respuestas sección y lo aceptó como se sugiere.

¿Fue útil?

Solución

Gracias a bits y piezas de varias respuestas, creo que podemos coser una explicación.

Al intentar imprimir una cadena Unicode, u ' xe9', Python intenta implícitamente codificar esa cadena usando el esquema de codificación actualmente almacenado en sys.stdout.Encoding. Python en realidad recoge este entorno del entorno del que se ha iniciado. Si no puede encontrar una codificación adecuada del entorno, solo entonces vuelve a su defecto, Ascii.

Por ejemplo, uso un shell bash que codifica predeterminado a UTF-8. Si empiezo a Python desde él, se acelera y usa esa configuración:

$ python

>>> import sys
>>> print sys.stdout.encoding
UTF-8

Vamos a salir por un momento de Python Shell y establecer el entorno de Bash con una codificación falsa:

$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.

Luego comience nuevamente el shell de Python y verifique que realmente vuelva a su codificación ASCII predeterminada.

$ python

>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968

¡Bingo!

Si ahora intenta generar un carácter unicode fuera de ASCII, debe recibir un buen mensaje de error

>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' 
in position 0: ordinal not in range(128)

Salgamos de Python y desechemos el caparazón de la fiesta.

Ahora observaremos lo que sucede después de que Python emite cadenas. Para esto, primero comenzaremos una carcasa de bash dentro de un terminal gráfico (uso el terminal GNOME) y estableceremos el terminal en la salida de decodificación con ISO-8859-1 AKA LATIN-1 (los terminales gráficos generalmente tienen una opción para Establecer la codificación de personajes en uno de sus menús desplegables). Tenga en cuenta que esto no cambia el real Shell Environment's codificación, solo cambia la forma en que el Terminal en sí mismo decodificará la salida que se da, un poco como lo hace un navegador web. Por lo tanto, puede cambiar la codificación del terminal, independientemente del entorno del caparazón. Entonces comencemos a Python desde el shell y verifiquemos que sys.stdout.Encoding se establece en la codificación del entorno de shell (UTF-8 para mí):

$ python

>>> import sys

>>> print sys.stdout.encoding
UTF-8

>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>

(1) Python emite una cadena binaria tal como está, Terminal lo recibe e intenta que coincida con su valor con el mapa de caracteres Latin-1. En Latin-1, 0xe9 o 233 produce el personaje "é" y eso es lo que muestra la terminal.

(2) Python intenta implícitamente Codifique la cadena Unicode con cualquier esquema que se establezca actualmente en sys.stdout.encoding, en este caso es "UTF-8". Después de la codificación UTF-8, la cadena binaria resultante es ' xc3 xa9' (ver explicación posterior). Terminal recibe la corriente como tal e intenta decodificar 0xC3A9 usando Latin-1, pero Latin-1 va de 0 a 255 y, por lo tanto, solo decodifica los transmisiones 1 byte a la vez. 0xc3a9 es 2 bytes de largo, el decodificador latino-1, por lo tanto, lo interpreta como 0xc3 (195) y 0xa9 (169) y eso produce 2 caracteres: ã y ©.

(3) Python codifica el punto de código Unicode u ' xe9' (233) con el esquema Latin-1. Resulta que el rango de puntos de código LATIN-1 es 0-255 y señala exactamente el mismo carácter que Unicode dentro de ese rango. Por lo tanto, los puntos de código Unicode en ese rango producirán el mismo valor cuando se codifique en Latin-1. Entonces U ' xe9' (233) codificado en Latin-1 también producirá la cadena binaria ' xe9'. Terminal recibe ese valor e intenta igualarlo en el mapa de caracteres Latin-1. Al igual que Case (1), produce "é" y eso es lo que se muestra.

Ahora cambiemos la configuración de codificación del terminal a UTF-8 desde el menú desplegable (como cambiaría la configuración de codificación de su navegador web). No es necesario detener a Python o reiniciar la carcasa. La codificación del terminal ahora coincide con la de Python. Intentemos imprimir de nuevo:

>>> print '\xe9' # (4)

>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)

>>>

(4) Python emite un binario cadena como es. Terminal intenta decodificar esa corriente con UTF-8. Pero UTF-8 no entiende el valor 0xe9 (ver explicación posterior) y, por lo tanto, no puede convertirlo en un punto de código Unicode. No se encontró ningún punto de código, sin carácter impreso.

(5) Python intenta implícitamente Codifique la cadena Unicode con lo que esté en sys.stdout.Encoding. Todavía "UTF-8". La cadena binaria resultante es ' xc3 xa9'. El terminal recibe la secuencia e intenta decodificar 0xC3A9 también usando UTF-8. Rinde el valor del código de retroceso 0xe9 (233), que en el mapa de caracteres Unicode apunta al símbolo "é". Terminal muestra "é".

(6) Python codifica una cadena Unicode con Latin-1, produce una cadena binaria con el mismo valor ' xe9'. Nuevamente, para el terminal, este es más o menos lo mismo que el caso (4).

Conclusiones: - Python emite cadenas no unicode como datos sin procesar, sin considerar su codificación predeterminada. El terminal simplemente los muestra si su codificación actual coincide con los datos. - Python emite cadenas Unicode después de codificarlas usando el esquema especificado en sys.stdout.Encoding. - Python obtiene esa configuración del entorno de la concha. - El terminal muestra la salida de acuerdo con su propia configuración de codificación. - La codificación del terminal es independiente del caparazón.


Más detalles sobre Unicode, UTF-8 y Latin-1:

Unicode es básicamente una tabla de caracteres donde algunas claves (puntos de código) han sido asignadas convencionalmente para señalar algunos símbolos. Por ejemplo, por convención se ha decidido que la clave 0xe9 (233) es el valor que apunta al símbolo 'é'. ASCII y Unicode usan los mismos puntos de código de 0 a 127, al igual que Latin-1 y Unicode de 0 a 255. Es decir, 0x41 puntos a 'A' en ASCII, Latin-1 y Unicode, 0xc8 apunta a 'ü' en Latin-1 y Unicode, 0xe9 apunta a 'É' en Latin-1 y Unicode.

Cuando se trabaja con dispositivos electrónicos, los puntos de código Unicode necesitan una forma eficiente de representarse electrónicamente. De eso se tratan los esquemas de codificación. Existen varios esquemas de codificación unicode (UTF7, UTF-8, UTF-16, UTF-32). El enfoque de codificación más intuitivo y directo sería simplemente usar el valor de un punto de código en el mapa Unicode como su valor para su forma electrónica, pero Unicode actualmente tiene más de un millón de puntos de código, lo que significa que algunos de ellos requieren que sean 3 bytes para ser expresado. Para trabajar de manera eficiente con el texto, un mapeo 1 a 1 no sería práctico, ya que requeriría que todos los puntos de código se almacenen exactamente en la misma cantidad de espacio, con un mínimo de 3 bytes por carácter, independientemente de su necesidad real.

La mayoría de los esquemas de codificación tienen deficiencias con respecto al requisito de espacio, los más económicos no cubren todos los puntos de código Unicode, por ejemplo, ASCII solo cubre los primeros 128, mientras que Latin-1 cubre los primeros 256. Otros que intentan ser más completos también. Ser un desperdicio, ya que requieren más bytes de los necesarios, incluso para personajes comunes "baratos". UTF-16, por ejemplo, usa un mínimo de 2 bytes por carácter, incluidos los del rango ASCII ('B' que es 65, todavía requiere 2 bytes de almacenamiento en UTF-16). UTF-32 es aún más derrochador, ya que almacena todos los personajes en 4 bytes.

UTF-8 resuelve inteligentemente el dilema, con un esquema capaz de almacenar puntos de código con una cantidad variable de espacios de byte. Como parte de su estrategia de codificación, UTF-8 cordera los puntos de código con bits de bandera que indican (presumiblemente a los decodificadores) sus requisitos de espacio y sus límites.

UTF-8 Codificación de puntos de código Unicode en el rango ASCII (0-127):

0xxx xxxx  (in binary)
  • Las X muestran el espacio real reservado para "almacenar" el punto de código durante la codificación
  • El 0 líder es una bandera que indica al decodificador UTF-8 que este punto de código solo requerirá 1 byte.
  • Al codificar, UTF-8 no cambia el valor de los puntos de código en ese rango específico (es decir, 65 codificados en UTF-8 también es 65). Teniendo en cuenta que Unicode y ASCII también son compatibles en el mismo rango, por cierto, hace que UTF-8 y ASCII también también compatible en ese rango.

Por ejemplo, el punto de código Unicode para 'B' es '0x42' o 0100 0010 en binario (como dijimos, es lo mismo en ASCII). Después de codificar en UTF-8 se convierte en:

0xxx xxxx  <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010  <-- Unicode code point 0x42
0100 0010  <-- UTF-8 encoded (exactly the same)

UTF-8 Codificación de puntos de código Unicode por encima de 127 (no ASCII):

110x xxxx 10xx xxxx            <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx  <-- (from 2048 to 65535)
  • Los bits principales '110' indican al decodificador UTF-8 el comienzo de un punto de código codificado en 2 bytes, mientras que '1110' indica 3 bytes, 11110 indicarían 4 bytes, etc.
  • Los bits de bandera '10' internos se utilizan para indicar el comienzo de un byte interno.
  • Nuevamente, las X marcan el espacio donde se almacena el valor del punto de código Unicode después de codificar.

Por ejemplo, el punto de código unicode 'é' es 0xe9 (233).

1110 1001    <-- 0xe9

Cuando UTF-8 codifica este valor, determina que el valor es mayor que 127 y menos de 2048, por lo tanto, debe codificarse en 2 bytes:

110x xxxx 10xx xxxx   <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001   <-- 0xe9
1100 0011 1010 1001   <-- 'é' after UTF-8 encoding
C    3    A    9

El código de unicode 0xe9 después de la codificación UTF-8 se convierte en 0xc3a9. Así es exactamente como lo recibe el terminal. Si su terminal está configurado para decodificar cadenas usando Latin-1 (una de las codificaciones heredadas no Unicode), verá ©, porque sucede que 0xc3 en latín-1 apunta a ã y 0xa9 a ©.

Otros consejos

Cuando los caracteres unicode se imprimen a stdout, sys.stdout.encoding se usa. Se supone que un personaje no unicodo está en sys.stdout.encoding y se acaba de enviar a la terminal. En mi sistema (Python 2):

>>> import unicodedata as ud
>>> import sys
>>> sys.stdout.encoding
'cp437'
>>> ud.name(u'\xe9') # U+00E9 Unicode codepoint
'LATIN SMALL LETTER E WITH ACUTE'
>>> ud.name('\xe9'.decode('cp437')) 
'GREEK CAPITAL LETTER THETA'
>>> '\xe9'.decode('cp437') # byte E9 decoded using code page 437 is U+0398.
u'\u0398'
>>> ud.name(u'\u0398')
'GREEK CAPITAL LETTER THETA'
>>> print u'\xe9' # Unicode is encoded to CP437 correctly
é
>>> print '\xe9'  # Byte is just sent to terminal and assumed to be CP437.
Θ

sys.getdefaultencoding() Solo se usa cuando Python no tiene otra opción.

Tenga en cuenta que Python 3.6 o posterior ignora las codificaciones en Windows y usa API de Unicode para escribir Unicode en el terminal. No se muestran las advertencias de UnicodeEndodeRor y el carácter correcto si la fuente la admite. Incluso si la fuente no Apoyarlo, los caracteres aún se pueden cortar desde el terminal a una aplicación con una fuente de soporte y será correcto. ¡Mejora!

El Python ReplA intenta recoger qué codificación usar desde su entorno. Si encuentra algo sano, entonces todo funciona. Es cuando no puede entender lo que está pasando, lo que resuelve.

>>> print sys.stdout.encoding
UTF-8

tener especificó una codificación ingresando una cadena unicode explícita. Compare los resultados de no usar el u prefijo.

>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> '\xe9'
'\xe9'
>>> u'\xe9'
u'\xe9'
>>> print u'\xe9'
é
>>> print '\xe9'

>>> 

En el caso de \xe9 Entonces Python asume su codificación predeterminada (ASCII), imprimiendo así ... algo en blanco.

Esto funciona para mi:

import sys
stdin, stdout = sys.stdin, sys.stdout
reload(sys)
sys.stdin, sys.stdout = stdin, stdout
sys.setdefaultencoding('utf-8')

Según Python predeterminado/codificaciones de cadena implícita y conversiones :

  • Cuando printEn g unicode, su encodeD con <file>.encoding.
    • cuando el encoding no está configurado, el unicode se convierte implícitamente en str (ya que el códec para eso es sys.getdefaultencoding(), es decir ascii, cualquier personaje nacional causaría un UnicodeEncodeError)
    • Para transmisiones estándar, el encoding se infiere del medio ambiente. Por lo general, se establece FOT tty transmisiones (desde la configuración local del terminal), pero es probable que no se establezcan para tuberías
      • Entonces un print u'\xe9' es probable que tenga éxito cuando la salida es a un terminal y falla si se redirige. Una solución es encode() la cadena con la codificación deseada antes printEn g.
  • Cuando printEn g str, los bytes se envían a la transmisión tal como está. Qué glifos muestra el terminal dependerá de su configuración local.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top