UnicodeDecodeError: o codec 'ascii' não pode decodificar o byte 0xd1 na posição 2: ordinal fora do intervalo (128)

107

Estou tentando trabalhar com um conjunto de dados muito grande que contém alguns caracteres fora do padrão. Preciso usar Unicode, de acordo com as especificações do trabalho, mas estou perplexo. (E muito possivelmente fazendo tudo errado.)

Eu abro o CSV usando:

 15     ncesReader = csv.reader(open('geocoded_output.csv', 'rb'), delimiter='\t', quotechar='"')

Então, tento codificá-lo com:

name=school_name.encode('utf-8'), street=row[9].encode('utf-8'), city=row[10].encode('utf-8'), state=row[11].encode('utf-8'), zip5=row[12], zip4=row[13],county=row[25].encode('utf-8'), lat=row[22], lng=row[23])

Estou codificando tudo, exceto lat e lng porque eles precisam ser enviados para uma API. Quando executo o programa para analisar o conjunto de dados no que posso usar, obtenho o seguinte Traceback.

Traceback (most recent call last):
  File "push_into_db.py", line 80, in <module>
    main()
  File "push_into_db.py", line 74, in main
    district_map = buildDistrictSchoolMap()
  File "push_into_db.py", line 32, in buildDistrictSchoolMap
    county=row[25].encode('utf-8'), lat=row[22], lng=row[23])
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 2: ordinal not in range(128)

Acho que devo dizer que estou usando o python 2.7.2, e isso faz parte de um aplicativo desenvolvido no django 1.4. Eu li vários posts sobre este tópico, mas nenhum deles parece se aplicar diretamente. Qualquer ajuda será muito apreciada.

Você também pode querer saber se alguns dos caracteres não padrão que causam o problema são Ñ e possivelmente É.

jelkimantis
fonte
1
Qual é a codificação do arquivo original? Acho que você deve decodificá-lo de acordo com a codificação original e depois converter para utf 8
xiao 啸
possível duplicata de Codificação dá "codec 'ascii' não pode codificar caractere ... ordinal fora do intervalo (128)" [Ed .: e de aproximadamente um zilhão de outros também, tenho certeza.]
Karl Knechtel

Respostas:

152

Unicode não é igual a UTF-8. O último é apenas uma codificação para o primeiro.

Você está fazendo isso da maneira errada. Você está lendo dados codificados em UTF-8 , portanto, é necessário decodificar a String codificada em UTF-8 em uma string Unicode.

Portanto, basta substituir .encodepor .decodee deve funcionar (se o seu .csv for codificado em UTF-8).

Nada para se envergonhar, no entanto. Aposto que 3 em 5 programadores tiveram problemas para entender isso, se não mais;)

Atualização: se os seus dados de entrada não forem codificados em UTF-8, você precisará .decode()usar a codificação apropriada, é claro. Se nada for fornecido, o python assume ASCII, o que obviamente falha em caracteres não ASCII.

ch3ka
fonte
1
O motivo do erro é que o Python está tentando decodificá-lo automaticamente da codificação padrão, ASCII, para que possa então codificá-lo conforme especificado, para UTF-8. Como os dados não são ASCII válidos, isso não funciona.
agf
7
claro, mas se forem dados codificados em UTF8 (como eu acho), então .decode('utf-8')deve servir, né ?
ch3ka
Claro, você provavelmente está certo. Eu estava apenas explicando por que você obtém esse erro específico nesta situação.
agf
1
Perfeito! Muito obrigado. Acontece que era .decode ('latin-1') - isso faz sentido porque era Ñ que estava me causando o problema. Novamente! Obrigado!
jelkimantis
Sua solução funciona em alguns casos, mas no caso de eu usar isso, recebo outro erro o codec 'ascii' não pode codificar o caractere u '\ xf1' na posição 2: ordinal fora do intervalo (128)
Vikash Mishra
84

Basta adicionar estas linhas aos seus códigos:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
khelili miliana
fonte
5
`AttributeError: módulo 'sys' não tem atributo 'setdefaultencoding' não parece funcionar no Python 3
skjerns
Woot woot! Isso me ajudou.
Shougo Makishima
1
Funciona no meu Python 2.7, note, reload (sys) é necessário, caso contrário, setdefaultencoding não estaria acessível.
Yu Shen de
1
Essa foi a única coisa que fez funcionar para mim em muitas perguntas SO. Muito obrigado!
Freedo
nome 'reload' não está definido
Davide
28

para usuários do Python 3. você pode fazer

with open(csv_name_here, 'r', encoding="utf-8") as f:
    #some codes

funciona com o frasco também :)

Skrmnghrd
fonte
1
É a primeira vez que ajudo alguém por aqui. é bom saber que ajudei :)
Skrmnghrd
1
E você também me ajudou :) Todas as outras respostas não funcionaram para leitura de arquivo. Agora preciso descobrir como consertá-lo também para escrever;)
user2194898
você pode me enviar o link do seu código? Vou tentar ajudar
Skrmnghrd
9

O principal motivo do erro é que a codificação padrão assumida pelo python é ASCII. Portanto, se os dados da string a serem codificados por encode('utf8')contiverem caracteres fora do intervalo ASCII, por exemplo, para uma string como 'hgvcj 터 파크 387', o python geraria um erro porque a string não está no formato de codificação esperado.

Se você estiver usando a versão python anterior à versão 3.5, uma correção confiável seria definir a codificação padrão assumida pelo python para utf8:

import sys
reload(sys)
sys.setdefaultencoding('utf8')
name = school_name.encode('utf8')

Dessa forma, o python seria capaz de antecipar caracteres dentro de uma string fora do intervalo ASCII.

No entanto, se você estiver usando o python versão 3.5 ou superior, a função reload () não está disponível, então você terá que corrigi-lo usando decodificar, por exemplo

name = school_name.decode('utf8').encode('utf8')
Temi Fakunle
fonte
qual é a diferença entre a sua resposta e a minha
khelili miliana
1
Mais detalhado. Muitas vezes as pessoas consideram os detalhes causais úteis. E seu código funciona btw, sem intenção de derrogação.
Temi Fakunle
1
reload está disponível no Python 3, você apenas teria que importá-lo. de imp importação reload
Meow
@Meow, mas não há sys.setdefaultencoding no Python 3. Portanto, no contexto de compatibilidade py2 \ py3, alguma verificação servirá, sys.getdefaultencoding () talvez. Gostaria de receber um conselho sobre esse assunto. stackoverflow.com/questions/28127513/…
Konst54
2

Para usuários do Python 3:

mudar a codificação de 'ascii' para 'latin1' funciona.

Além disso, você pode tentar encontrar a codificação automaticamente lendo os 10.000 bytes principais usando o snippet abaixo:

import chardet  
with open("dataset_path", 'rb') as rawdata:  
            result = chardet.detect(rawdata.read(10000))  
print(result)
Prithvi
fonte
2

Meu computador tinha o local errado definido.

Eu fiz primeiro

>>> import locale
>>> locale.getpreferredencoding(False)
'ANSI_X3.4-1968'

locale.getpreferredencoding(False)é a função chamada por open()quando você não fornece uma codificação . A saída deve ser 'UTF-8', mas neste caso é alguma variante do ASCII .

Então eu executei o comando bash localee obtive esta saída

$ locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Então, eu estava usando a localidade padrão do Ubuntu, que faz com que o Python abra arquivos como ASCII em vez de UTF-8. Eu tive que definir minha localidade paraen_US.UTF-8

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales

Se você não pode alterar a localidade do sistema, você pode invocar todo o seu código Python assim:

PYTHONIOENCODING="UTF-8" python3 ./path/to/your/script.py

ou faça

export PYTHONIOENCODING="UTF-8"

para configurá-lo no shell em que você o executa.

Boris
fonte
1

se você tiver esse problema ao executar o certbot ao criar ou renovar o certificado, use o seguinte método

grep -r -P '[^\x00-\x7f]' /etc/apache2 /etc/letsencrypt /etc/nginx

Esse comando encontrou o caractere ofensivo "´" em um arquivo .conf no comentário. Depois de removê-lo (você pode editar os comentários como desejar) e recarregar o nginx, tudo funcionou novamente.

Fonte: https://github.com/certbot/certbot/issues/5236

Anish Varghese
fonte
0

Ou quando você lida com texto em Python, se for um texto Unicode, observe que é Unicode.

Defina text=u'unicode text'apenas text='unicode text'.

Isso funcionou no meu caso.

Prosti
fonte
0

aberto com codificação UTF 16 por causa de latitude e longitude.

with open(csv_name_here, 'r', encoding="utf-16") as f:
Karthik r
fonte
0

Ele funciona apenas tomando o argumento 'rb' ler binário em vez de 'r' ler

Jose Garcia-Uceda
fonte