Python: el códec 'ascii' no puede decodificar el byte

119

Estoy realmente confundido. Traté de codificar pero el error decía 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)

Sé cómo evitar el error con el prefijo "u" en la cadena. Me pregunto por qué el error es "no se puede decodificar" cuando se llamó a encode. ¿Qué está haciendo Python bajo el capó?

thoslin
fuente

Respuestas:

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

encodeconvierte un objeto Unicode en un stringobjeto. Pero aquí lo ha invocado en un stringobjeto (porque no tiene la u). Entonces, Python tiene que convertir primero stringen un unicodeobjeto. Entonces hace el equivalente de

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

Pero la decodificación falla porque la cadena no es ascii válida. Es por eso que recibe una queja por no poder decodificar.

Winston Ewert
fuente
50
¿Entonces, cuál es la solución? Especialmente si no tengo un literal de cadena, solo tengo un objeto de cadena.
Jon Tirsen
2
@JonTirsen, no debería codificar un objeto de cadena. Un objeto de cadena ya está codificado. Si necesita cambiar la codificación, debe decodificarla en una cadena Unicode y luego codificarla como la codificación deseada.
Winston Ewert
20
Entonces, para expresarlo claramente desde arriba, puede"你好".decode('utf-8').encode('utf-8')
deinonychusaur
5
@WinstonEwert Supongo que estaba confundido. El negocio de la codificación tiende a dejarme eternamente confundido. Supongo que mi confusión proviene de mi propio problema de no saber si la entrada es una cadena o una cadena Unicode y qué codificación puede tener.
deinonychusaur
@deinonychusaur, sí ... lo entiendo.
Winston Ewert
53

Codifique siempre de Unicode a bytes.
En esta dirección, puedes elegir la codificación .

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

La otra forma es decodificar de bytes a Unicode.
En esta dirección, debes saber cuál es la codificación .

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

Este punto no se puede enfatizar lo suficiente. Si desea evitar jugar unicode "whack-a-mole", es importante comprender lo que sucede a nivel de datos. Aquí se explica de otra forma:

  • Un objeto Unicode ya está decodificado, nunca querrás llamarlo decode.
  • Un objeto de cadena de bytes ya está codificado, nunca querrás llamarlo encode.

Ahora, al ver .encodeuna cadena de bytes, Python 2 primero intenta convertirlo implícitamente en texto (un unicodeobjeto). De manera similar, al ver .decodeuna cadena Unicode, Python 2 implícitamente intenta convertirla en bytes (un strobjeto).

Estas conversiones implícitas son la razón por la que puede obtener cuando ha llamado . Es porque la codificación generalmente acepta un parámetro de tipo ; al recibir un parámetro, hay una decodificación implícita en un objeto de tipo antes de volver a codificarlo con otra codificación. Esta conversión elige un decodificador 'ascii' predeterminado , lo que le da el error de decodificación dentro de un codificador.UnicodeDecodeErrorencodeunicodestrunicode

De hecho, en Python 3 los métodos str.decodey bytes.encodeni siquiera existen. Su remoción fue un intento [controvertido] de evitar esta confusión común.

... o lo que sys.getdefaultencoding()mencione la codificación ; generalmente esto es 'ascii'

wim
fuente
Entonces, ¿quiere decir que Python decodifica la cadena de bytes antes de codificar?
thoslin
@thoslin exactamente, agregué más detalles.
wim
¿Qué es _ y por qué faltan paréntesis en sus declaraciones impresas?
NoBugs
1
@NoBugs 1. en el REPL, se _refiere al valor anterior 2. porque esta es una pregunta de python-2.x.
wim
40

Puedes probar esto

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

O

También puedes intentar seguir

Agregue la siguiente línea en la parte superior de su archivo .py.

# -*- coding: utf-8 -*- 
Dadaso Zanzane
fuente
8

Si está utilizando Python <3, deberá decirle al intérprete que su literal de cadena es Unicode prefijándolo conu :

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'

Más información : Unicode HOWTO .

Johnsyweb
fuente
4
Si está codificando una cadena, ¿por qué arroja un error de decodificación?
MxLDevs
3

Se utiliza u"你好".encode('utf8')para codificar una cadena Unicode. Pero si quieres representar "你好", debes decodificarlo. Al igual que:

"你好".decode("utf8")

Obtendrás lo que quieras. Quizás debería aprender más sobre codificar y decodificar.

Qingtian
fuente
3

En caso de que esté tratando con Unicode, a veces en lugar de encode('utf-8'), también puede intentar ignorar los caracteres especiales, por ejemplo

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

o como se something.decode('unicode_escape').encode('ascii','ignore')sugiere aquí .

No es particularmente útil en este ejemplo, pero puede funcionar mejor en otros escenarios cuando no es posible convertir algunos caracteres especiales.

Alternativamente, puede considerar reemplazar un carácter particular usandoreplace() .

Kenorb
fuente
1

Si está iniciando el intérprete de Python desde un shell en Linux o sistemas similares (BSD, no estoy seguro de Mac), también debe verificar la codificación predeterminada del shell.

Llame locale charmapdesde el shell (no el intérprete de Python) y debería ver

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

Si este no es el caso y ve algo más, p. Ej.

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

Python heredará (al menos en algunos casos, como en el mío) la codificación del shell y no podrá imprimir (¿algunos? ¿Todos?) Caracteres Unicode. La propia codificación predeterminada de Python que ve y controla a través de, sys.getdefaultencoding()y sys.setdefaultencoding()en este caso se ignora.

Si encuentra que tiene este problema, puede solucionarlo

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

(O, alternativamente, elija el mapa de teclas que desee en lugar de en_EN). También puede editar /etc/locale.conf(o cualquier archivo que gobierne la definición de configuración regional en su sistema) para corregir esto.

0rango
fuente