Python - Le codec 'ascii' ne peut pas décoder l'octet

119

Je suis vraiment confus. J'ai essayé d'encoder mais l'erreur a dit can't decode....

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

Je sais comment éviter l'erreur avec le préfixe "u" sur la chaîne. Je me demande simplement pourquoi l'erreur est "impossible de décoder" lorsque l'encode a été appelé. Que fait Python sous le capot?

Thoslin
la source

Réponses:

167
"你好".encode('utf-8')

encodeconvertit un objet Unicode en stringobjet. Mais ici, vous l'avez invoqué sur un stringobjet (car vous n'avez pas le u). Donc, python doit d'abord convertir le stringen unicodeobjet. Donc ça fait l'équivalent de

"你好".decode().encode('utf-8')

Mais le décodage échoue car la chaîne n'est pas valide ascii. C'est pourquoi vous recevez une plainte pour ne pas pouvoir décoder.

Winston Ewert
la source
50
Alors, quelle est la solution? Surtout si je n'ai pas de chaîne littérale, j'ai juste un objet chaîne.
Jon Tirsen
2
@JonTirsen, vous ne devriez pas encoder un objet chaîne. Un objet chaîne est déjà codé. Si vous avez besoin de changer le codage, vous devez le décoder en une chaîne Unicode, puis le coder en tant que codage souhaité.
Winston Ewert
20
Donc, pour le dire clairement d'en haut, vous pouvez"你好".decode('utf-8').encode('utf-8')
deinonychusaur
5
@WinstonEwert J'imagine que j'étais confus. Les affaires d'encodage ont tendance à me laisser éternellement confus. Je suppose que ma confusion est venue de mon propre problème de ne pas savoir si l'entrée est une chaîne ou une chaîne unicode et quel encodage elle peut avoir.
deinonychusaur
@deinonychusaur, ouais ... je comprends ça.
Winston Ewert
53

Encodez toujours de Unicode en octets.
Dans cette direction, vous choisissez l'encodage .

>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print _
你好

L'autre façon consiste à décoder des octets en unicode.
Dans ce sens, vous devez savoir quel est le codage .

>>> bytes = '\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print bytes
你好
>>> bytes.decode('utf-8')
u'\u4f60\u597d'
>>> print _
你好

Ce point ne peut pas être assez souligné. Si vous voulez éviter de jouer unicode "whack-a-mole", il est important de comprendre ce qui se passe au niveau des données. Ici, il est expliqué d'une autre manière:

  • Un objet Unicode est déjà décodé, vous ne voulez jamais l'appeler decode.
  • Un objet bytestring est déjà encodé, vous ne voulez jamais l'appeler encode.

Maintenant, en voyant .encodeune chaîne d'octets, Python 2 essaie d'abord de la convertir implicitement en texte (un unicodeobjet). De même, en voyant .decodesur une chaîne unicode, Python 2 essaie implicitement de la convertir en octets (un strobjet).

Ces conversions implicites sont la raison pour laquelle vous pouvez obtenir lorsque vous avez appelé . C'est parce que l'encodage accepte généralement un paramètre de type ; lors de la réception d'un paramètre, il y a un décodage implicite dans un objet de type avant de le recoder avec un autre encodage. Cette conversion choisit un décodeur «ascii» par défaut , vous donnant l'erreur de décodage à l'intérieur d'un encodeur.UnicodeDecodeErrorencodeunicodestrunicode

En fait, en Python 3 , les méthodes str.decodeet bytes.encoden'existent même pas. Leur suppression était une tentative [controversée] d'éviter cette confusion courante.

... ou tout ce que le codage sys.getdefaultencoding()mentionne; il s'agit généralement de 'ascii'

wim
la source
Voulez-vous dire que Python décode le bytestring avant l'encodage?
thoslin
@thoslin exactement, j'ai ajouté plus de détails.
wim
Qu'est-ce que _ et pourquoi vos instructions d'impression n'ont-elles pas de parenthèses?
NoBugs
1
@NoBugs 1. dans la REPL, _fait référence à la valeur précédente 2. car c'est une question python-2.x.
wim
40

Vous pouvez essayer ceci

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

Ou

Vous pouvez également essayer de suivre

Ajoutez la ligne suivante en haut de votre fichier .py.

# -*- coding: utf-8 -*- 
Dadaso Zanzane
la source
8

Si vous utilisez Python <3, vous devrez dire à l'interpréteur que votre chaîne littérale est Unicode en la préfixant avec unu :

Python 2.7.2 (default, Jan 14 2012, 23:14:09) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> "你好".encode("utf8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'

Lectures complémentaires : HOWTO Unicode .

Johnsyweb
la source
4
Si vous encodez une chaîne, pourquoi génère-t-elle une erreur de décodage?
MxLDevs
3

Vous utilisez u"你好".encode('utf8')pour encoder une chaîne unicode. Mais si vous voulez représenter "你好", vous devez le décoder. Juste comme:

"你好".decode("utf8")

Vous obtiendrez ce que vous voulez. Vous devriez peut-être en savoir plus sur l'encodage et le décodage.

Qingtian
la source
3

Dans le cas où vous avez affaire à Unicode, parfois au lieu de encode('utf-8'), vous pouvez également essayer d'ignorer les caractères spéciaux, par exemple

"你好".encode('ascii','ignore')

ou comme something.decode('unicode_escape').encode('ascii','ignore')suggéré ici .

Pas particulièrement utile dans cet exemple, mais peut mieux fonctionner dans d'autres scénarios lorsqu'il n'est pas possible de convertir certains caractères spéciaux.

Vous pouvez également envisager de remplacer un caractère particulier en utilisantreplace() .

Kenorb
la source
1

Si vous démarrez l'interpréteur python à partir d'un shell sur Linux ou des systèmes similaires (BSD, pas sûr pour Mac), vous devez également vérifier l'encodage par défaut pour le shell.

Appelez locale charmapdepuis le shell (pas l'interpréteur python) et vous devriez voir

[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

Si ce n'est pas le cas et que vous voyez autre chose, par exemple

[user@host dir] $ locale charmap
ANSI_X3.4-1968
[user@host dir] $ 

Python héritera (au moins dans certains cas comme dans le mien) du codage du shell et ne pourra pas imprimer (certains? Tous?) Les caractères Unicode. L'encodage par défaut de Python que vous voyez et contrôlez via sys.getdefaultencoding()etsys.setdefaultencoding() est dans ce cas ignoré.

Si vous constatez que vous rencontrez ce problème, vous pouvez le résoudre en

[user@host dir] $ export LC_CTYPE="en_EN.UTF-8"
[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

(Ou bien choisissez le keymap que vous voulez au lieu de en_EN.) Vous pouvez également éditer /etc/locale.conf(ou le fichier qui régit la définition des paramètres régionaux dans votre système) pour corriger cela.

0 plage
la source