Python: Conversion d'ISO-8859-1 / latin1 en UTF-8

87

J'ai cette chaîne qui a été décodée de Quoted-printable à ISO-8859-1 avec le module email. 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?

Zyberzero
la source

Réponses:

122

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

apple.decode('iso-8859-1').encode('utf8')
Tapis
la source
5
J'ai eu quelques problèmes pour encoder des trucs dans ma langue (portugais), donc ce qui a fonctionné pour moi était string.decode ('iso-8859-1'). Encode ('latin1'). De plus, en haut de mon fichier python, j'ai ce code # - - codage: latin-1 - -
Moon13
148

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

Pour les chaînes non-unicode (c'est-à-dire celles sans upréfixe comme u'\xc4pple'), il faut décoder à partir du codage natif ( iso8859-1/ latin1, sauf si modifié avec lasys.setdefaultencoding fonction énigmatique ) vers unicode, puis encoder dans un jeu de caractères qui peut afficher les caractères que vous souhaitez, dans ce cas je Je recommande UTF-8.

Tout d'abord, voici une fonction utilitaire pratique qui vous aidera à éclairer les modèles de chaîne Python 2.7 et d'Unicode:

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

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

Décoder une chaîne iso8859-1 - convertir une chaîne simple 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 l'encodage spécifique (latin-1, utf8, utf16) en unicode par exemple u8.decode('utf8').encode('latin1').

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

  • un type strest un ensemble d'octets, qui peut avoir l'un des nombreux codages tels que Latin-1, UTF-8 et UTF-16
  • un type unicodeest un ensemble d'octets qui peuvent être convertis en n'importe quel nombre d'encodages, le plus souvent UTF-8 et latin-1 (iso8859-1)
  • la printcommande a sa propre logique d'encodage , définie sur sys.stdout.encodinget par défaut sur UTF-8
  • Il faut décoder un stren unicode avant de le 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 diatribes très illustratives d'Armin Ronacher:

Brian M. Hunt
la source
12
Merci d'avoir pris le temps d'écrire une explication aussi détaillée, l'une des meilleures réponses que j'ai jamais trouvées sur stackoverflow :)
ruyadorno
5
Sensationnel. Succinct, très compréhensible et expliqué par l'exemple. Merci d'avoir amélioré les Intertubes.
Monkey Boson
22

Pour Python 3:

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

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

Michal Skop
la source
d'où bytesvient-il?
alvas
1
Documentation: octets . Voir également cette question et ses réponses.
Michal Skop
3
Pour les fichiers téléchargés avec des demandes avec des en-têtes manquants ou incorrects: r = requests.get(url)puis le réglage direct a r.encoding = 'utf-8'fonctionné pour moi
Michal Skop
documentation de la méthode bytes.decode .
mike
10

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

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

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

Shashank Agarwal
la source