UnicodeDecodeError: o codec 'utf8' não pode decodificar o byte 0x9c


289

Eu tenho um servidor de soquete que deveria receber caracteres válidos UTF-8 dos clientes.

O problema é que alguns clientes (principalmente hackers) estão enviando todo o tipo errado de dados sobre ele.

Posso distinguir facilmente o cliente genuíno, mas estou registrando nos arquivos todos os dados enviados para poder analisá-los mais tarde.

Às vezes, recebo caracteres como esse œque causam o UnicodeDecodeErrorerro.

Eu preciso ser capaz de criar a string UTF-8 com ou sem esses caracteres.


Atualizar:

Para meu caso particular, o serviço de soquete era um MTA e, portanto, espero receber apenas comandos ASCII, como:

EHLO example.com
MAIL FROM: <john.doe@example.com>
...

Eu estava registrando tudo isso no JSON.

Então algumas pessoas por aí, sem boas intenções, decidiram vender todo tipo de lixo.

É por isso que, no meu caso específico, é perfeitamente aceitável remover os caracteres não ASCII.


1
a string sai de um arquivo ou soquete? você poderia postar exemplos de código de como a string é codificada e decodificada antes de ser enviada pelo socket / filehandler?
devsnd 17/09/12

Escrevi ou não escrevi que a string vem sobre o soquete? Eu simplesmente leio a string do soquete e coloquei-a em um dicionário e depois JSON para enviá-la. A função JSON falhou devido a esses caracteres.
Transilvlad

Você pode por favor colocar seus dados de amostra de problema
Shubham Sharma

Respostas:


343

http://docs.python.org/howto/unicode.html#the-unicode-type

str = unicode(str, errors='replace')

ou

str = unicode(str, errors='ignore')

Nota: Isso removerá (ignorará) os caracteres em questão retornando a sequência sem eles.

Para mim, esse é o caso ideal, pois estou usando-o como proteção contra entradas não-ASCII, que não são permitidas pelo meu aplicativo.

Como alternativa: use o método open do codecsmódulo para ler no arquivo:

import codecs
with codecs.open(file_name, 'r', encoding='utf-8',
                 errors='ignore') as fdata:

45
Sim, embora isso geralmente seja uma prática ruim / perigosa, porque você só perde personagens. Melhor determinar ou detectar a codificação da sequência de entrada e decodificá-la para unicode primeiro, depois codifique como UTF-8, por exemplo:str.decode('cp1252').encode('utf-8')
Ben Hoyt

Em alguns casos, sim, você está certo, isso pode causar problemas. No meu caso, não me importo com eles, pois eles parecem caracteres extras originários de uma má formatação e programação dos clientes conectados ao meu servidor de soquete.
transilvlad

Este realmente ajuda se o conteúdo da seqüência é realmente inválido, no meu caso '\xc0msterdam', que se transforma em u'\ufffdmsterdam'com substitua
PvdL

3
se você acabou aqui porque está tendo problemas para ler um arquivo, abrir o arquivo no modo binário pode ajudar: open(file_name, "rb")e depois aplicar a abordagem de Ben nos comentários acima
kristian

a mesma opção se aplica a ainda mais, por exemplo, "something.decode ()"
Alexander Stohr

83

Mudar o mecanismo de C para Python fez o truque para mim.

O motor é C:

pd.read_csv(gdp_path, sep='\t', engine='c')

O codec 'utf-8' não pode decodificar o byte 0x92 na posição 18: byte inicial inválido

O mecanismo é Python:

pd.read_csv(gdp_path, sep='\t', engine='python')

Sem erros para mim.


3
essa é realmente uma boa solução. Eu não sei por que foi votado.
ℕʘʘḆḽḘ

Isso pode não ser uma boa ideia se você tiver um csvarquivo enorme . Isso pode levar a um OutOfMemoryerro ou a uma reinicialização automática do kernel do seu notebook. Você deve definir encodingneste caso.
LucasBr

1
Excelente resposta. Obrigado. Isso funcionou para mim. Eu tinha "?" Dentro de um caractere de formato de diamante que estava causando o problema. Com olhos claros eu tinha "" "que é polegadas. Eu fiz duas coisas para descobrir. a) df = pd.read_csv ('test.csv', n_rows = 10000). Isso funcionou perfeitamente sem o motor. Então eu incrementei o n_rows para descobrir qual linha teve erro. b) df = pd.read_csv ('test.csv', mecanismo = 'python'). Isso funcionou e eu imprimi a linha com erro usando df.iloc [36145], isso me imprimiu o registro com erro.
Jagannath Banerjee

1
isso funcionou para mim também ... Não tenho certeza do que está acontecendo "sob o capô" e se essa é realmente uma solução boa / boa / adequada em todos os casos, mas funcionou para mim;)
Chrisvdberge

1
Ótima solução! Muito obrigado.
Pechi 21/04

62

Esse tipo de problema surge para mim agora que mudei para o Python 3. Eu não fazia ideia de que o Python 2 estava simplesmente lançando problemas com a codificação de arquivos.

Encontrei esta boa explicação sobre as diferenças e como encontrar uma solução depois que nenhuma das opções acima funcionou para mim.

http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html

Em resumo, para fazer com que o Python 3 se comporte da maneira mais semelhante possível ao uso do Python 2:

with open(filename, encoding="latin-1") as datafile:
    # work on datafile here

No entanto, leia o artigo, não existe uma solução única para todos os tamanhos.


29
>>> '\x9c'.decode('cp1252')
u'\u0153'
>>> print '\x9c'.decode('cp1252')
œ

16
Estou confuso, como você escolheu o cp1252? Funcionou para mim, mas por quê? Não sei e agora estou perdido:. Você poderia elaborar? Muito obrigado ! :)
Cyril N.

4
Você poderia apresentar uma opção que funcione para todos os personagens? Existe uma maneira de detectar os caracteres que precisam ser decodificados para que um código mais genérico possa ser implementado? Vejo muitas pessoas olhando para isso e aposto que algumas devoluções não são a opção desejada, como é para mim.
transilvlad

Como você pode ver, essa pergunta tem muita popularidade. Pense que você poderia expandir sua resposta com uma solução mais genérica?
transilvlad

13
Não existe uma solução mais genérica para "Adivinhar a roleta de codificação"
Puppy

5
encontrou-lo usando uma combinação de pesquisa na web, sorte e intuição: CP1252 foiused by default in the legacy components of Microsoft Windows in English and some other Western languages
bolov

24

Eu tive o mesmo problema UnicodeDecodeErrore resolvi-o com esta linha. Não sei se é o melhor caminho, mas funcionou para mim.

str = str.decode('unicode_escape').encode('utf-8')

13

o primeiro, usando get_encoding_type para obter o tipo de arquivo de codificação:

import os    
from chardet import detect

# get file encoding type
def get_encoding_type(file):
    with open(file, 'rb') as f:
        rawdata = f.read()
    return detect(rawdata)['encoding']

o segundo, abrindo os arquivos com o tipo:

open(current_file, 'r', encoding = get_encoding_type, errors='ignore')

1
o que acontece quando ele retorna None
Chop Labalagun 27/01

3

Apenas no caso de alguém tem o mesmo problema. Estou usando o vim com o YouCompleteMe , falha ao iniciar o ycmd com esta mensagem de erro, o que fiz foi: export LC_CTYPE="en_US.UTF-8"o problema desapareceu.


2
Como isso se relaciona com essa pergunta?
Transilvlad

1
Exatamente o mesmo, se você souber como o complemento funciona. YCM plugin é tomada arquitetura, comunicação entre cliente e servidor está usando socket, ambos são módulos python, não é capaz de decodificar os pacotes, se a configuração de codificação está incorreta
workplaylifecycle

Eu tenho o mesmo problema. Você pode me dizer onde colocar export LC_CTYPE="en_US.UTF-8"?
Reman

@Remonn oi, você sabe que temos arquivo de perfil para bash? Ponha dentro.
workplaylifecycle

@hylepo, eu estou em um sistema Windows :)
Reman

3

O que você pode fazer se precisar alterar um arquivo, mas não souber a codificação do arquivo? Se você sabe que a codificação é compatível com ASCII e deseja examinar ou modificar apenas as partes ASCII, é possível abrir o arquivo com o manipulador de erros surrogateescape:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

0

Eu resolvi esse problema apenas adicionando

df = pd.read_csv(fileName,encoding='latin1')
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.