web-dev-qa-db-fra.com

Python: Conversion de ISO-8859-1/latin1 à UTF-8

J'ai cette chaîne qui a été décodée de Quoted-printable à ISO-8859-1 avec le module de messagerie. Cela me donne des chaînes comme "\ xC4pple" qui correspondraient à "Äpple" (Apple en suédois) . Cependant, je ne peux pas convertir ces chaînes en 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)

Que devrais-je faire?

76
Zyberzero

Essayez d'abord de le décoder, puis d'encoder:

Apple.decode('iso-8859-1').encode('utf8')
101
Mat

C'est un problème courant, voici donc une illustration relativement complète.

Pour les chaînes non-unicode (celles sans préfixe u comme u'\xc4pple'), il faut décoder à partir du codage natif (iso8859-1/latin1, sauf si modifié avec l'énigmatique sys.setdefaultencoding fonction) en unicode , puis encoder jeu de caractères pouvant afficher les caractères souhaités, dans ce cas, je recommanderais UTF-8 .

Premièrement, voici une fonction utilitaire pratique qui vous aidera à éclairer les modèles de chaîne Python 2.7 et de code Unicode:

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

Une ficelle unie

>>> 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 "?".

Décodage d'une chaîne iso8859-1 - Conversion de la chaîne standard en 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 peu plus d'illustration - avec “Ä”

>>> 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').

Encodage en 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')

Relation entre unicode et UTF et 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)

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

On pourrait les contourner en convertissant le codage spécifique (latin-1, utf8, utf16) en unicode, par ex. u8.decode('utf8').encode('latin1').

Alors peut-être pourrait-on tirer les principes et généralisations suivants:

  • un type str est un ensemble d'octets, qui peuvent avoir l'un des nombreux codages tels que Latin-1, UTF-8 et UTF-16.
  • un type unicode est un ensemble d'octets pouvant être convertis en un nombre quelconque de codages, le plus souvent en UTF-8 et en latin-1 (iso8859-1)
  • la commande print a sa propre logique de codage , définie sur sys.stdout.encoding et définie par défaut sur UTF-8
  • Il faut décoder une str en unicode avant de convertir en un autre encodage.

Bien sûr, tout cela change dans Python 3.x.

J'espère que c'est éclairant.

Lectures complémentaires

Et les discours très illustratifs d'Armin Ronacher:

134
Brian M. Hunt

Pour Python 3:

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

Je l'ai utilisé pour un texte incorrectement codé en iso-8859-1 (montrant des mots comme VeÅ\x99ejné ) au lieu de utf-8. Ce code produit une version correcte Veřejné .

12
Michal Skop

Décodez en Unicode, encodez les résultats en UTF8. 

Apple.decode('latin1').encode('utf8')
10
jd.
concept = concept.encode('ascii', 'ignore') 
concept = MySQLdb.escape_string(concept.decode('latin1').encode('utf8').rstrip())

Je fais cela, je ne suis pas sûr que ce soit une bonne approche mais ça marche à chaque fois !!

0
Shashank Agarwal