Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Pb encodage ?

10 réponses
Avatar
Michel Claveau - MVP
Bonsoir !

Je ne comprends pas ce TraceBack :

txt=contenu.encode('utf-8','ignore')
open(filename+".bak","wb").write(txt)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 11676: ordinal not in range(128)

surtout que u'\xe9' est un simple é, qui ne devrait pas poser de problème à écrire dans un fichier.

Une idée ?

@+
--
Michel Claveau

10 réponses

Avatar
Francois Lafont
Le 11/09/2010 21:31, Michel Claveau - MVP a écrit :
Bonsoir !



Salut,

Je ne comprends pas ce TraceBack :

txt=contenu.encode('utf-8','ignore')
open(filename+".bak","wb").write(txt)
UnicodeEncodeError: 'ascii' codec can't encode character u'xe9' in position 11676: ordinal not in range(128)

surtout que u'xe9' est un simple é, qui ne devrait pas poser de problème à écrire dans un fichier.

Une idée ?




Bizarre. Es-tu sûr que la chaîne que tu mets dans la méthode .write()
est bien de type 'str' et non de type 'unicode' ? Dans ton exemple de
code c'est le cas bien sûr, mais est-ce bien le cas dans ton "vrai" code
(j'image que pour le poste tu as essayé de simplifier le problème) ?

Je pose cette question car ton erreur fait vraiment penser à ceci : si
dans fichier.write(txt), txt est de type 'unicode' et donc n'a pas été
encodé par tes soins, alors je crois que python tente d'encoder txt à la
volée en 'ascii' par défaut et donc, devant la présence d'un 'é', lève
une exception.


--
François Lafont
Avatar
Bruno Desthuilliers
Michel Claveau - MVP a écrit :
Bonsoir !

Je ne comprends pas ce TraceBack :

txt=contenu.encode('utf-8','ignore')
open(filename+".bak","wb").write(txt)
UnicodeEncodeError: 'ascii' codec can't encode character u'xe9' in position 11676: ordinal not in range(128)



Ca aurait été bien que tu postes effectivement le traceback - pas
seulement le message d'erreur :-/

surtout que u'xe9' est un simple é, qui ne devrait pas poser de problème à écrire dans un fichier.



Comme on n'a pas le traceback, je suis bien obligé de poser la question:
c'est bien l'appel à file.write qui lève l'exception ??? Ou c'est pas
plutôt l'appel à contenu.encode ?

s = "ééé"
u = u"ééé"
t = s.encode('utf-8', 'ignore')






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)
t = u.encode('utf-8', 'ignore')







Une idée ?




Oui.

txt=contenu.encode('utf-8','ignore')
fname = filename + ".bak"
f = open(fname,"wb")
try:
f.write(txt)
finally:
f.close()

D'une part ça te permet d'être sûr de ne pas tomber à court de
descripteurs de fichiers, et d'autre part ça permet, si l'erreur n'est
pas sur contenu.encode, de savoir précisément quelle expression / appel
provoque cette exception.
Avatar
Francois Lafont
Le 13/09/2010 11:25, Bruno Desthuilliers a écrit :

Comme on n'a pas le traceback, je suis bien obligé de poser la question:
c'est bien l'appel à file.write qui lève l'exception ??? Ou c'est pas
plutôt l'appel à contenu.encode ?

s = "ééé"
u = u"ééé"
t = s.encode('utf-8', 'ignore')






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)



Moi qui pensais être enfin un peu au clair sur ces histoires
d'encodages, je m'aperçois que non.

Je ne comprends pas trop 1) ce que fait exactement t = s.encode('utf-8',
'ignore') et 2) pourquoi on a une erreur.

1) À vrai dire je pensais que .encode() n'existait que pour le type
'unicode' et pas pour le type 'str'. Je ne comprends pas trop l'intérêt
d'encoder une chaîne de type 'str' qui l'est déjà. De plus, je ne
comprends pas comment peut marcher cette méthode si on ne précise pas
quelque part l'encodage de la chaîne au départ. Pour moi, ceci à un sens :

chaine_unicode.encode('encodage_voulu')

ou bien :

my_string.decode('encodage_de_my_string').encode('encodage_voulu')

Mais je ne comprends le sens de ça :

* my_string.encode('encodage_voulu')

sachant qu'on ne précise même pas l'encodage de my_string. Pour encoder
la chaîne vers 'encodage_voulu', il faut bien connaître l'encodage de
départ de my_string, non ?

Quelque chose m'échappe avec cette méthode .encode() lorsqu'elle est
appelée sur un objet de type 'str' ? Quelle est la différence entre
.encode() appelée sur un type 'str' et .encode() appelée sur un type
'unicode' ?


2) Comment t = s.encode('utf-8', 'ignore') peut-il provoquer une erreur
alors qu'on a mis 'ignore'. D'ailleurs, en faisant quelques essais, j'ai
toujours eu une erreur sauf lorsque s était une chaine ASCII (et du coup
je vois encore moins l'intérêt d'encoder une chaine ASCII).


Je serais intéressé d'avoir des explications sur ces deux points.



--
François Lafont
Avatar
Bruno Desthuilliers
Francois Lafont a écrit :
Le 13/09/2010 11:25, Bruno Desthuilliers a écrit :


s = "ééé"
u = u"ééé"
t = s.encode('utf-8', 'ignore')






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)



Moi qui pensais être enfin un peu au clair sur ces histoires
d'encodages, je m'aperçois que non.



Mais si, mais si !-)

La preuve : tu poses les bonnes question... auxquelles je n'ai aucune
réponse puisque je me pose exactement les mêmes (questions) :-/

Le seul point que je voulais soulever était justement que dans la mesure
où str.encode existe bel et bien, et avec le comportement que tu a
remarqué, il était facile d'avoir des mauvaises surprises avec...

(snip)
Avatar
jmfauth
Dans Python 2, il y deux types de string, le type str et le type
unicode.
Le type unicode et le module codecs ayant été introduits en cours
d'évolution de Python 2, de légères incohérences sont apparues, en
fait assez mineures et facilement contournables puisque qu'elles
dépendent
plus de la logique que de Python lui même.

type('abc')






<type 'str'>
type(u'abc')






<type 'unicode'>

Dans le jargon Python, encode() signifie: "prendre un unicode et en
faire un
str".

u = unicode('abc', 'cp1252')
s = u.encode('utf-8')
type(u)






<type 'unicode'>
type(s)






<type 'str'>

Et decode() signifie: "prendre un str et en faire un unicode".

s = 'ax00bx00cx00xe9x00'
u = s.decode('utf-16-le')
type(s)






<type 'str'>
type(u)






<type 'unicode'>
repr(u)






u'abcxe9'

Parmi les codecs disponibles, il en existes qui "transforment
des str en str".

s = 'abc'
s2 = s.encode('hex')
type(s)






<type 'str'>
type(s2)






<type 'str'>
s2






616263

Les flags des fonctions encode() ou decode(), args optionels,
fonctionnent bien, il faut cependant les utiliser dans un
contexte logique (et de compréhension de ce qu'est le
codage des caractères).

u'abcéz'.encode('ascii')






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'xe9' in
position 3: ordinal not in range(128)
u'abcéz'.encode('ascii', 'ignore')






abcz
u'abcéz'.encode('ascii', 'replace')






abc?z

Dans les cas ambigüs, Python 2 s'accomode très bien avec
les "caractères ascii" et génère une erreur pour les
"caractères non ascii".

Dans les exemples suivants, une erreur est lévée non
pas parce qu'un "é" ne puisse pas "être codé en utf-8 ou utf-16",
mais parce que un str ne peut être converti en unicode avec la
fct encode(). Les flags sont donc d'aucune utilité.

'é'.encode('utf-8')






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
position 0: ordinal not in range(128)
'é'.encode('utf-8', 'ignore')






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
position 0: ordinal not in range(128)
'é'.encode('utf-8', 'replace')






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
position 0: ordinal not in range(128)

Mais

'a'.encode('utf-8', 'replace')






a
'a'.encode('utf-8', 'ignore')






a
'a'.encode('utf-8')






a

repr('a'.encode('utf-16'))






'xffxfeax00'
repr('é'.encode('utf-16'))






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
position 0: ordinal not in range(128)

=============

Dans Python 3, la gestion des strings est rigoureusement
identique à Python 2. Python 3 a cependant permis un "grand
nettoyage" et une gestion plus stricte.

Il y a toujours deux types de strings, le type str et le type
bytes (pendants respectifs de unicode et str en Python 2).

type('abc')






<class 'str'>
type(b'abc')






<class 'bytes'>








La gestion des "transcodages" se fait proprement.

Un type str ne peut être qu' en-codé et ne peut être
dé-codé.
'abc'.encode('utf-8')






b'abc'
'abc'.decode






Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
'abc'.decode
AttributeError: 'str' object has no attribute 'decode'

Inversément, un type bytes ne peut être que dé-codé et
non en-codé.

b'abc'.decode('ascii')






'abc'
b'abc'.encode






Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
b'abc'.encode
AttributeError: 'bytes' object has no attribute 'encode'

Les codecs permettant une conversion de codage de type
bytes -> bytes ont disparus. L'utilisation des "list
comprehension" permettant aisément de se sortir d'affaire.


=============

Sous réserve d'une grosse bourde de ma part, cela devrait
être correct.

jmf
Avatar
Francois Lafont
Le 14/09/2010 11:53, Bruno Desthuilliers a écrit :

Moi qui pensais être enfin un peu au clair sur ces histoires
d'encodages, je m'aperçois que non.



Mais si, mais si !-)

La preuve : tu poses les bonnes question... auxquelles je n'ai aucune
réponse puisque je me pose exactement les mêmes (questions) :-/



Ah, alors ça me rassure. :-)


--
François Lafont
Avatar
Francois Lafont
Le 14/09/2010 12:23, jmfauth a écrit :

Parmi les codecs disponibles, il en existes qui "transforment
des str en str".

s = 'abc'
s2 = s.encode('hex')
type(s)






<type 'str'>
type(s2)






<type 'str'>
s2






616263



Ah oui, voilà enfin un codec tel que chaine_str.encode('hex') ne lève
pas une exception, même si chaine_str n'est pas 100% ASCII.

Dans les exemples suivants, une erreur est lévée non
pas parce qu'un "é" ne puisse pas "être codé en utf-8 ou utf-16",
mais parce que un str ne peut être converti en unicode avec la
fct encode().



Heu, pour convertir un str en unicode, c'est decode() plutôt, non ?

'é'.encode('utf-8')






Traceback (most recent call last):
File "<psi last command>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
position 0: ordinal not in range(128)



Cet exemple et le message d'erreur qui va avec me donnent l'impression que :

«'é'.encode('utf8')» équivaut à «'é'.decode('ascii').encode('utf8')»

et que, du coup, je ne vois vraiment pas l'intérêt.
En revanche 'é'.encode('hex') fonctionne et n'est donc pas équivalent à
ce que j'ai écrit ci-dessus. Du coup, c'est pas très très dans ma tête
tout ça, mais je me dis qu'un encodage str -> str, ça ne me servira pas
tous les jours et ...

La gestion des "transcodages" se fait proprement.

Un type str ne peut être qu'en-codé et ne peut être dé-codé.

[...]

Inversément, un type bytes ne peut être que dé-codé et non en-codé.



... là, en Python 3, ça me va. C'est clair au moins. :-)

Les codecs permettant une conversion de codage de type
bytes -> bytes ont disparus.



Donc, ça ne vaut peut-être pas le coup que je m'attarde sur l'encodage
'str' -> 'str' de Python 2.x.

Sous réserve d'une grosse bourde de ma part, cela devrait
être correct.



Merci beaucoup jmf pour toutes ces explications.



--
François Lafont
Avatar
jmfauth
On 14 sep, 20:46, Francois Lafont
wrote:
Le 14/09/2010 12:23, jmfauth a écrit :

> Dans les exemples suivants, une erreur est lévée non
> pas parce qu'un "é" ne puisse pas "être codé en utf-8 ou utf-16",
> mais parce que un str ne peut être converti en unicode avec la
> fct encode().

Heu, pour convertir un str en unicode, c'est decode() plutôt, non ?



Oui et non. Disons que pour *générer* un unicode à partir d'un str
avec decode(), il faut obligatoirement que le str représente un
des codages valides de l'unicode.

Pour *créer* un unicode, on utilise unicode(bytestring,
codage_de_bytestring).



>>>> 'é'.encode('utf-8')
> Traceback (most recent call last):
>   File "<psi last command>", line 1, in <module>
> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
> position 0: ordinal not in range(128)

Cet exemple et le message d'erreur qui va avec me donnent l'impression qu e :

«'é'.encode('utf8')» équivaut à «'é'.decode('ascii').encode ('utf8')»




C'est tout à fait cela. Avant d'encoder en 'utf-8', il faut d'abord
créer un unicode valide. Un «'é'.encode('utf8')» ne peut le faire
correctement que si "encode()" connaît le codage de "é", hors il
ne le connaît pas et assumme un codage 'ascii'.

L'équivalent précis est:

'é'.decode('ascii', 'strict').encode('utf-8')

La façon propre de travailler est de le faire en 2 étapes.

u = unicode('é', 'iso-8859-1')
s = u.encode('utf-8')
type(u), type(s), repr(s)






(<type 'unicode'>, <type 'str'>, "'xc3xa9'")
type(u), repr(u), type(s), repr(s)






(<type 'unicode'>, "u'xe9'", <type 'str'>, "'xc3xa9'")


De toute façon, ce n'est pas très logique de vouloir
directement créer un "utf-8" à partir d'un str, car
seuls les unicodes ont des codages. En interne, En Python 2,
un str n'est pas un unicode, par contre en Python 3, ça l'est.


jmf
Avatar
jmfauth
On 14 sep, 23:11, jmfauth wrote:
On 14 sep, 20:46, Francois Lafont
wrote:

> Le 14/09/2010 12:23, jmfauth a écrit :

> > Dans les exemples suivants, une erreur est lévée non
> > pas parce qu'un "é" ne puisse pas "être codé en utf-8 ou utf-16 ",
> > mais parce que un str ne peut être converti en unicode avec la
> > fct encode().


> >>>> 'é'.encode('utf-8')
> > Traceback (most recent call last):
> >   File "<psi last command>", line 1, in <module>
> > UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in
> > position 0: ordinal not in range(128)

> Cet exemple et le message d'erreur qui va avec me donnent l'impression que :

> «'é'.encode('utf8')» équivaut à «'é'.decode('ascii').enco de('utf8')»

C'est tout à fait cela. Avant d'encoder en 'utf-8', il faut d'abord
créer un unicode valide. Un «'é'.encode('utf8')» ne peut le faire
correctement que si "encode()" connaît le codage de "é", hors il
ne le connaît pas et assumme un codage 'ascii'.

L'équivalent précis est:

'é'.decode('ascii', 'strict').encode('utf-8')

La façon propre de travailler est de le faire en 2 étapes.

>>> u = unicode('é', 'iso-8859-1')
>>> s = u.encode('utf-8')
>>> type(u), type(s), repr(s)

(<type 'unicode'>, <type 'str'>, "'xc3xa9'")>>> type(u), repr(u), typ e(s), repr(s)

(<type 'unicode'>, "u'xe9'", <type 'str'>, "'xc3xa9'")

De toute façon, ce n'est pas très logique de vouloir
directement créer un "utf-8" à partir d'un str, car
seuls les unicodes ont des codages. En interne, En Python 2,
un str n'est pas un unicode, par contre en Python 3, ça l'est.

jmf



edit: suppression d'une phrase mal formulée.
Avatar
Michel Claveau - MVP
Bonsoir !

En fait, le problème n'arrivant jamais sur une autre machine, j'ai
effectué une mise à jour de Python, ce qui a fait disparaitre le
soucis (c'était une vieille 2.4.x)

Merci à tous.
--
Michel Claveau