¿Por qué la codificación de una cadena da como resultado un error de DEcodificación (UnicodeDecodeError)?

6 minutos de lectura

avatar de usuario de thoslin
thoslin

Estoy realmente confundido. Intenté 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 codificar. ¿Qué está haciendo Python debajo del capó?

  • Tenga en cuenta que este problema se solucionó en 3.x: las cadenas son simplemente Unicode y bytes los objetos son simplemente bytes en bruto, y nunca los dos se encontrarán. Las cadenas no realizan ninguna decodificación implícita cuando .encode se llama; De hecho, .encode es no soportado en absoluto, ya que no tiene sentido. El extraño comportamiento solo estaba presente en 2.x como un truco de compatibilidad en primer lugar, debido a la forma en que se introdujo Unicode en el lenguaje.

    – Karl Knechtel

    4 de enero a las 6:46

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

encode convierte un objeto Unicode en un string objeto. Pero aquí lo has invocado en un string objeto (porque no tienes la u). Así que Python tiene que convertir el string a un unicode objeto primero. 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 sobre no poder decodificar.

  • ¿Entonces, cuál es la solución? Especialmente si no tengo un literal de cadena, solo tengo un objeto de cadena.

    – Jon Tirsen

    12 de marzo de 2013 a las 7:57


  • @JonTirsen, no debe 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

    12 de marzo de 2013 a las 16:24

  • Entonces, para decirlo claramente desde arriba, puedes "你好".decode('utf-8').encode('utf-8')

    – deinonychusaurio

    25 de julio de 2013 a las 8:18

  • @WinstonEwert Supongo que estaba confundido. El negocio de la codificación tiende a dejarme eternamente confundido. Supongo que mi confusión provino de mi propio problema de no saber si la entrada es una cadena o una cadena Unicode y qué codificación puede tener.

    – deinonychusaurio

    31 de julio de 2013 a las 17:36

  • @deinonychusaur, sí… lo entiendo.

    –Winston Ewert

    31 de julio de 2013 a las 18:19

avatar de usuario de wim
ingenio

Siempre codificar 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, tienes que 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 quiere evitar jugar unicode “whack-a-mole”, es importante comprender lo que sucede a nivel de datos. Aquí se explica de otra manera:

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

Ahora, al ver .encode en una cadena de bytes, Python 2 primero intenta convertirlo implícitamente en texto (un unicode objeto). Del mismo modo, al ver .decode en una cadena Unicode, Python 2 implícitamente intenta convertirla en bytes (un str objeto).

Estas conversiones implícitas son la razón por la que puede obtener UnicodeDecodeError cuando has llamado encode. Es porque la codificación generalmente acepta un parámetro de tipo unicode; al recibir un str parámetro, hay una decodificación implícita en un objeto de tipo unicode antes de volver a codificarlo con otra codificación. Esta conversión elige un decodificador ‘ascii’ predeterminadodándote el error de decodificación dentro de un codificador.

De hecho, en Python 3 los métodos str.decode y bytes.encode ni siquiera existen. Su remoción fue un [controversial] tratar de evitar esta confusión común.

… o cualquier codificación sys.getdefaultencoding() menciones; por lo general esto es ‘ascii’

  • Entonces, ¿quiere decir que Python decodifica la cadena de bytes antes de codificar?

    – Thoslin

    10 de marzo de 2012 a las 5:28

  • @thoslin exactamente, agregué más detalles.

    – Wim

    02/06/2014 a las 18:00

  • ¿Qué es _ y por qué faltan paréntesis en sus declaraciones impresas?

    – Sin errores

    30 de septiembre de 2017 a las 3:24

  • @NoBugs 1. en el REPL, _ se refiere al valor anterior 2. porque esta es una pregunta de python-2.x.

    – Wim

    30 de septiembre de 2017 a las 3:37


  • Gracias por la pista. Para mi caso, la solución correcta fue simplemente .decode(‘utf-8’). Ejecuté Subprocess.popen(…).communicate() que devolvió bytes que contenían caracteres alemanes ä, ö, ü y el .decode() normal (sin el parámetro ‘utf-8’) falló. Con el parámetro ‘utf-8’ funciona.

    – Alexander Samoylov

    20 mayo 2022 a las 13:25


Avatar de usuario de Dadaso Zanzane
Dadaso Zanzane

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 -*- 

  • ¡Esta debe ser una respuesta aceptada!

    – Alexei

    29 de abril de 2022 a las 9:29

avatar de usuario de johnsyweb
johnsyweb

Si está utilizando Python literal de cadena es Unicode prefijándolo con un u:

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'

Otras lecturas: CÓMO Unicode.

Avatar de usuario de Qingtian
Qingtian

Tu usas u"你好".encode('utf8') para codificar una cadena Unicode. Pero si quieres representar "你好", deberías decodificarlo. Al igual que:

"你好".decode("utf8")

Obtendrás lo que quieres. Tal vez debería aprender más sobre codificar y decodificar.

avatar de usuario de kenorb
kenorb

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 something.decode('unicode_escape').encode('ascii','ignore') como se 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 usando replace().

Avatar de usuario de 0range
0rango

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

Llamada locale charmap desde 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, por ejemplo

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

Python (al menos en algunos casos, como en el mío) heredará 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 descubre 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.

¿Ha sido útil esta solución?