Python - o codec 'ascii' não pode decodificar bytes


119

Estou realmente confuso. Tentei codificar mas o erro disse 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)

Eu sei como evitar o erro com o prefixo "u" na string. Só estou me perguntando por que o erro é "não é possível decodificar" quando encode foi chamado. O que o Python está fazendo nos bastidores?

Respostas:


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

encodeconverte um objeto Unicode em um stringobjeto. Mas aqui você o invocou em um stringobjeto (porque você não tem o u). Portanto, o python precisa primeiro converter o stringem um unicodeobjeto. Então, ele faz o equivalente a

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

Mas a decodificação falha porque a string não é ascii válida. É por isso que você recebe uma reclamação por não ser capaz de decodificar.


50
Então qual é a solução? Especialmente se eu não tiver um literal de string, apenas tenho um objeto de string.
Jon Tirsen

2
@JonTirsen, você não deve codificar um objeto string. Um objeto string já está codificado. Se você precisar alterar a codificação, deverá decodificá-la em uma string Unicode e, em seguida, codificá-la como a codificação desejada.
Winston Ewert

20
Então, para afirmar claramente de cima, você pode"你好".decode('utf-8').encode('utf-8')
deinoniquusaur

5
@WinstonEwert Acho que fiquei confuso. O negócio de codificação tende a me deixar eternamente confuso. Acho que minha confusão veio do meu próprio problema de não saber se a entrada é uma string ou string Unicode e qual codificação ela pode ter.
Deinonychusaur

@deinonychusaur, sim ... eu entendo.
Winston Ewert

53

Sempre codifique de Unicode para bytes.
Nesse sentido, você pode escolher a codificação .

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

A outra maneira é decodificar de bytes para Unicode.
Nesse sentido, você deve saber qual é a codificação .

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

Este ponto nunca pode ser enfatizado o suficiente. Se você quiser evitar jogar "whack-a-mole" unicode, é importante entender o que está acontecendo no nível dos dados. Aqui é explicado de outra maneira:

  • Um objeto Unicode já está decodificado, você nunca vai querer chamá decode-lo.
  • Um objeto bytestring já está codificado, você nunca vai querer chamá encode-lo.

Agora, ao ver .encodeuma string de bytes, Python 2 primeiro tenta convertê-la implicitamente em texto (um unicodeobjeto). Da mesma forma, ao ver .decodeuma string Unicode, o Python 2 tenta implicitamente convertê-la em bytes (um strobjeto).

Essas conversões implícitas são o motivo pelo qual você pode obter quando liga . É porque a codificação geralmente aceita um parâmetro do tipo ; ao receber um parâmetro, há uma decodificação implícita em um objeto do tipo antes de recodificá-lo com outra codificação. Esta conversão escolhe um decodificador 'ascii' padrão , dando a você o erro de decodificação dentro de um codificador.UnicodeDecodeErrorencodeunicodestrunicode

De fato, em Python 3 os métodos str.decodee bytes.encodenem sequer existem. Sua remoção foi uma tentativa [controversa] de evitar essa confusão comum.

... ou qualquer sys.getdefaultencoding()menção de codificação ; normalmente é 'ascii'


Então, você quer dizer que o Python decodifica o bytestring antes de codificar?
thoslin

@thoslin exatamente, adicionei mais detalhes.
wim

O que é _, e por que suas declarações de impressão estão faltando parênteses?
NoBugs

1
@NoBugs 1. no REPL, _refere-se ao valor anterior 2. porque esta é uma questão python-2.x.
wim

40

Você pode tentar isso

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

Ou

Você também pode tentar seguir

Adicione a seguinte linha no topo do seu arquivo .py.

# -*- coding: utf-8 -*- 

8

Se estiver usando Python <3, você precisará informar ao intérprete que seu literal de string é Unicode, prefixando-o comu :

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'

Leitura adicional : Unicode HOWTO .


4
Se você está codificando uma string, por que isso gera um erro de decodificação?
MxLDevs de

3

Você usa u"你好".encode('utf8')para codificar uma string Unicode. Mas se você quiser representar "你好", você deve decodificá-lo. Assim como:

"你好".decode("utf8")

Você vai conseguir o que deseja. Talvez você deva aprender mais sobre codificação e decodificação.


3

Caso você esteja lidando com Unicode, às vezes em vez de encode('utf-8'), você também pode tentar ignorar os caracteres especiais, por exemplo

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

ou conforme something.decode('unicode_escape').encode('ascii','ignore')sugerido aqui .

Não é particularmente útil neste exemplo, mas pode funcionar melhor em outros cenários quando não é possível converter alguns caracteres especiais.

Alternativamente, você pode considerar a substituição de um caractere específico usandoreplace() .


1

Se você estiver iniciando o interpretador python de um shell no Linux ou sistemas semelhantes (BSD, não tenho certeza sobre o Mac), você também deve verificar a codificação padrão para o shell.

Chame locale charmapdo shell (não do interpretador python) e você verá

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

Se este não for o caso, e você vir outra coisa, por exemplo

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

Python irá (pelo menos em alguns casos como no meu) herdar a codificação do shell e não será capaz de imprimir (alguns? Todos?) Caracteres Unicode. A própria codificação padrão do Python que você vê e controla por meio sys.getdefaultencoding()e sys.setdefaultencoding(), neste caso, é ignorada.

Se você achar que tem esse problema, pode consertá-lo

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

(Ou, alternativamente, escolha qualquer mapa de teclado que você deseja em vez de en_EN.) Você também pode editar /etc/locale.conf(ou qualquer arquivo que governe a definição de localidade em seu sistema) para corrigir isso.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.