Python: conversión de ISO-8859-1 / latin1 a UTF-8

87

Tengo esta cadena que ha sido decodificada de Quoted-printable a ISO-8859-1 con el módulo de correo electrónico. Esto me da cadenas como "\ xC4pple" que correspondería a "Äpple" (Apple en sueco). Sin embargo, no puedo convertir esas cadenas a UTF-8.

>>> apple = "\xC4pple"
>>> apple
'\xc4pple'
>>> apple.encode("UTF-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: ordinal not in     range(128)

¿Qué tengo que hacer?

Zyberzero
fuente

Respuestas:

122

Intente decodificarlo primero y luego codificarlo:

apple.decode('iso-8859-1').encode('utf8')
Estera
fuente
5
Tuve algunos problemas para codificar cosas en mi idioma (portugués), así que lo que funcionó para mí fue string.decode ('iso-8859-1'). Encode ('latin1'). Además, en la parte superior de mi archivo de Python, tengo este # - - codificación: latin-1 - -
Moon13
148

Este es un problema común, por lo que aquí hay una ilustración relativamente completa.

Para cadenas no Unicode (es decir, aquellas sin uprefijo como u'\xc4pple'), uno debe decodificar desde la codificación nativa ( iso8859-1/ latin1, a menos que se modifique con lasys.setdefaultencoding función enigmática ) y unicodeluego codificar a un conjunto de caracteres que pueda mostrar los caracteres que desee, en este caso yo lo recomendaría UTF-8.

Primero, aquí hay una función de utilidad útil que ayudará a iluminar los patrones de cadena de Python 2.7 y Unicode:

>>> def tell_me_about(s): return (type(s), s)

Una cuerda simple

>>> v = "\xC4pple" # iso-8859-1 aka latin1 encoded string

>>> tell_me_about(v)
(<type 'str'>, '\xc4pple')

>>> v
'\xc4pple'        # representation in memory

>>> print v
?pple             # map the iso-8859-1 in-memory to iso-8859-1 chars
                  # note that '\xc4' has no representation in iso-8859-1, 
                  # so is printed as "?".

Decodificación de una cadena iso8859-1: convierta una cadena simple a unicode

>>> uv = v.decode("iso-8859-1")
>>> uv
u'\xc4pple'       # decoding iso-8859-1 becomes unicode, in memory

>>> tell_me_about(uv)
(<type 'unicode'>, u'\xc4pple')

>>> print v.decode("iso-8859-1")
Äpple             # convert unicode to the default character set
                  # (utf-8, based on sys.stdout.encoding)

>>> v.decode('iso-8859-1') == u'\xc4pple'
True              # one could have just used a unicode representation 
                  # from the start

Un poco más de ilustración: con "Ä"

>>> u"Ä" == u"\xc4"
True              # the native unicode char and escaped versions are the same

>>> "Ä" == u"\xc4"  
False             # the native unicode char is '\xc3\x84' in latin1

>>> "Ä".decode('utf8') == u"\xc4"
True              # one can decode the string to get unicode

>>> "Ä" == "\xc4"
False             # the native character and the escaped string are
                  # of course not equal ('\xc3\x84' != '\xc4').

Codificación a UTF

>>> u8 = v.decode("iso-8859-1").encode("utf-8")
>>> u8
'\xc3\x84pple'    # convert iso-8859-1 to unicode to utf-8

>>> tell_me_about(u8)
(<type 'str'>, '\xc3\x84pple')

>>> u16 = v.decode('iso-8859-1').encode('utf-16')
>>> tell_me_about(u16)
(<type 'str'>, '\xff\xfe\xc4\x00p\x00p\x00l\x00e\x00')

>>> tell_me_about(u8.decode('utf8'))
(<type 'unicode'>, u'\xc4pple')

>>> tell_me_about(u16.decode('utf16'))
(<type 'unicode'>, u'\xc4pple')

Relación entre unicode y UTF y latin1

>>> print u8
Äpple             # printing utf-8 - because of the encoding we now know
                  # how to print the characters

>>> print u8.decode('utf-8') # printing unicode
Äpple

>>> print u16     # printing 'bytes' of u16
���pple

>>> print u16.decode('utf16')
Äpple             # printing unicode

>>> v == u8
False             # v is a iso8859-1 string; u8 is a utf-8 string

>>> v.decode('iso8859-1') == u8
False             # v.decode(...) returns unicode

>>> u8.decode('utf-8') == v.decode('latin1') == u16.decode('utf-16')
True              # all decode to the same unicode memory representation
                  # (latin1 is iso-8859-1)

Excepciones Unicode

 >>> u8.encode('iso8859-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0:
  ordinal not in range(128)

>>> u16.encode('iso8859-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0:
  ordinal not in range(128)

>>> v.encode('iso8859-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0:
  ordinal not in range(128)

Uno podría evitarlos convirtiendo de la codificación específica (latin-1, utf8, utf16) a Unicode, por ejemplo u8.decode('utf8').encode('latin1').

Entonces, tal vez uno podría dibujar los siguientes principios y generalizaciones:

  • un tipo stres un conjunto de bytes, que puede tener una de varias codificaciones, como Latin-1, UTF-8 y UTF-16
  • un tipo unicodees un conjunto de bytes que se puede convertir a cualquier número de codificaciones, más comúnmente UTF-8 y latin-1 (iso8859-1)
  • el printcomando tiene su propia lógica para codificar , configurado sys.stdout.encodingy predeterminado en UTF-8
  • Uno debe decodificar un stra unicode antes de convertirlo a otra codificación.

Por supuesto, todo esto cambia en Python 3.x.

Espero que sea esclarecedor.

Otras lecturas

Y las muy ilustrativas peroratas de Armin Ronacher:

Brian M. Hunt
fuente
12
Gracias por tomarse el tiempo para escribir una explicación tan detallada, una de las mejores respuestas que he encontrado en stackoverflow :)
ruyadorno
5
Guau. Conciso, muy comprensible y explicado con un ejemplo. Gracias por mejorar los Intertubes.
Monkey Boson
22

Para Python 3:

bytes(apple,'iso-8859-1').decode('utf-8')

Usé esto para un texto codificado incorrectamente como iso-8859-1 (que muestra palabras como VeÅ \ x99ejnà © ) en lugar de utf-8. Este código produce la versión correcta Veřejné .

Michal Skop
fuente
de donde bytesviene
alvas
1
Documentación: bytes . Consulte también esta pregunta y sus respuestas.
Michal Skop
3
Para archivos descargados con solicitudes con encabezados faltantes o incorrectos: r = requests.get(url)y luego la configuración directa r.encoding = 'utf-8'funcionó para mí
Michal Skop
documentación del método bytes.decode .
Mike
10

Decodificar a Unicode, codificar los resultados a UTF8.

apple.decode('latin1').encode('utf8')
jd.
fuente
0
concept = concept.encode('ascii', 'ignore') 
concept = MySQLdb.escape_string(concept.decode('latin1').encode('utf8').rstrip())

Hago esto, no estoy seguro de si es un buen enfoque, ¡pero funciona siempre!

Shashank Agarwal
fuente