Il s'agit d'un problème courant, aussi en voici une illustration relativement complète.
Pour les chaînes non unicode (c'est-à-dire celles qui n'ont pas de u
préfixe comme u'\xc4pple'
), on doit décoder à partir de l'encodage natif ( iso8859-1
/ latin1
à moins que modifié avec l'énigmatique sys.setdefaultencoding
) pour unicode
puis encoder vers un jeu de caractères qui peut afficher les caractères que vous souhaitez, dans ce cas je recommanderais UTF-8
.
Tout d'abord, voici une fonction utilitaire pratique qui vous aidera à éclairer les modèles de chaîne et d'unicode de Python 2.7 :
>>> def tell_me_about(s): return (type(s), s)
Une chaîne de caractères 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écodage d'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 peut contourner ces problèmes en convertissant l'encodage spécifique (latin-1, utf8, utf16) en unicode, par exemple. u8.decode('utf8').encode('latin1')
.
On pourrait donc peut-être tirer les principes et généralisations suivants :
- un type
str
est un ensemble d'octets, qui peut avoir l'un des nombreux codages suivants : Latin-1, UTF-8 et UTF-16.
- un type
unicode
est un ensemble d'octets qui peuvent être convertis en un nombre quelconque de codages, le plus souvent UTF-8 et latin-1 (iso8859-1).
- le site
print
Le commandement a sa propre logique d'encodage fixé à sys.stdout.encoding
et par défaut UTF-8
- On doit décoder un
str
en unicode avant de le convertir en un autre encodage.
Bien entendu, tout cela change dans Python 3.x.
J'espère que cela vous éclairera.
Autres lectures
Et les coups de gueule très illustratifs d'Armin Ronacher :