Ler um arquivo UTF8 CSV com Python

93

Estou tentando ler um arquivo CSV com caracteres acentuados com Python (somente caracteres franceses e / ou espanhóis). Com base na documentação do Python 2.5 para o csvreader ( http://docs.python.org/library/csv.html ), criei o seguinte código para ler o arquivo CSV, já que o csvreader oferece suporte apenas para ASCII.

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')

filename = 'output.csv'
reader = unicode_csv_reader(open(filename))
try:
    products = []
    for field1, field2, field3 in reader:
        ...

Abaixo está um trecho do arquivo CSV que estou tentando ler:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert
...

Embora eu tente codificar / decodificar para UTF-8, ainda recebo a seguinte exceção:

Traceback (most recent call last):
  File ".\Test.py", line 53, in <module>
    for field1, field2, field3 in reader:
  File ".\Test.py", line 40, in unicode_csv_reader
    for row in csv_reader:
  File ".\Test.py", line 46, in utf_8_encoder
    yield line.encode('utf-8', 'ignore')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 68: ordinal not in range(128)

Como faço para corrigir isso?

Martin
fonte
Martin, se você estiver por perto, consideraria trocar a resposta aceita da única resposta de Martelli Python 2.
Antti Haapala

Respostas:

113

O .encodemétodo é aplicado a uma string Unicode para fazer uma string de bytes; mas você está chamando em uma string de bytes em vez ... do jeito errado! Consulte o codecsmódulo na biblioteca padrão e, codecs.openem particular, para melhores soluções gerais para a leitura de arquivos de texto codificados em UTF-8. No entanto, para o csvmódulo em particular, você precisa passar dados utf-8, e é isso que você já está recebendo, então seu código pode ser muito mais simples:

import csv

def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
    csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
    for row in csv_reader:
        yield [unicode(cell, 'utf-8') for cell in row]

filename = 'da.csv'
reader = unicode_csv_reader(open(filename))
for field1, field2, field3 in reader:
  print field1, field2, field3 

PS: se descobrir que seus dados de entrada NÃO estão em utf-8, mas, por exemplo, em ISO-8859-1, você precisa de uma "transcodificação" (se estiver interessado em usar utf-8 no csvnível do módulo) , da forma line.decode('whateverweirdcodec').encode('utf-8')- mas provavelmente você pode apenas usar o nome de sua codificação existente na yieldlinha em meu código acima, em vez de 'utf-8', como csvna verdade vai ficar bem com bytestrings codificados por ISO-8859- *.

Alex Martelli
fonte
4
Isso significa que o exemplo na documentação do Python (de onde OP copiar e colar) está errado? Qual é o objetivo da etapa de codificação extra que ele cumpre se quebra quando você atribui um csv Unicode?
Anentropic
80

Python 2.X

Existe uma biblioteca unicode-csv que deve resolver seus problemas, com o benefício adicional de não navegar para escrever nenhum novo código relacionado ao csv.

Aqui está um exemplo do readme:

>>> import unicodecsv
>>> from cStringIO import StringIO
>>> f = StringIO()
>>> w = unicodecsv.writer(f, encoding='utf-8')
>>> w.writerow((u'é', u'ñ'))
>>> f.seek(0)
>>> r = unicodecsv.reader(f, encoding='utf-8')
>>> row = r.next()
>>> print row[0], row[1]
é ñ

Python 3.X

No python 3, isso é compatível com o csvmódulo integrado. Veja este exemplo:

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
jb.
fonte
8

Se você quiser ler um arquivo CSV com codificação utf-8, uma abordagem minimalista que recomendo é usar algo assim:

with open(file_name, encoding="utf8") as csv_file:

Com essa declaração, você pode usar mais tarde um leitor CSV para trabalhar.

Nick Cuevas
fonte
2
É possível que este seja apenas Python 3? Ele falha para mim, em Python 2. Ele não aceita o encodinginopen
Zvika
@Zvika sim, no python 3 esta solução funciona:open('file.csv', 'r', encoding="ISO8859")
luca76
Eu também adicionaria open (file_name, "rt", encoding = 'utf-8'), isto é, abrir o arquivo no modo "ler texto"
Jimmy Lee Jones,
3

Verifique também a resposta nesta postagem: https://stackoverflow.com/a/9347871/1338557

Ele sugere o uso de uma biblioteca chamada ucsv.py. Substituição curta e simples para CSV escrito para resolver o problema de codificação (utf-8) para Python 2.7. Também oferece suporte para csv.DictReader

Editar : Adicionando código de amostra que usei:

import ucsv as csv

#Read CSV file containing the right tags to produce
fileObj = open('awol_title_strings.csv', 'rb')
dictReader = csv.DictReader(fileObj, fieldnames = ['titles', 'tags'], delimiter = ',', quotechar = '"')
#Build a dictionary from the CSV file-> {<string>:<tags to produce>}
titleStringsDict = dict()
for row in dictReader:
    titleStringsDict.update({unicode(row['titles']):unicode(row['tags'])})
Atripavan
fonte
você deve colocar alguns detalhes desse link em sua resposta, para o caso de o link quebrar \
Yaje
# Downvoter- Não tenho certeza por que você pensou que era inútil. A biblioteca ucsv funcionou muito bem para mim. Ajudou a resolver o erro unicde que eu estava lutando desde 2 dias. Se você estava procurando algum código de amostra, aqui vai na edição @ Yaje- Eu dei alguns detalhes; também o código de amostra. E corrigiu o link também, que antes apontava para outro post.
Atripavan
Algum motivo específico para abrir um arquivo de texto como binário? 'rb' serve para abrir arquivos binários.
Codeguy007
2

Usar o codecs.openque Alex Martelli sugeriu provou ser útil para mim.

import codecs

delimiter = ';'
reader = codecs.open("your_filename.csv", 'r', encoding='utf-8')
for line in reader:
    row = line.split(delimiter)
    # do something with your row ...
user1154664
fonte
3
Não funcionaria com todos os CSV, a seguir está uma linha csv válida: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.
Você importa o csvmódulo, mas não o usa.
Christophe Roussy
1

O link para a página de ajuda é o mesmo para o python 2.6 e, pelo que eu sei, não houve nenhuma mudança no módulo csv desde o 2.5 (além das correções de bugs). Aqui está o código que simplesmente funciona sem qualquer codificação / decodificação (o arquivo da.csv contém os mesmos dados que os dados variáveis ). Presumo que seu arquivo deve ser lido corretamente sem quaisquer conversões.

test.py:

## -*- coding: utf-8 -*-
#
# NOTE: this first line is important for the version b) read from a string(unicode) variable
#

import csv

data = \
"""0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert"""

# a) read from a file
print 'reading from a file:'
for (f1, f2, f3) in csv.reader(open('da.csv'), dialect=csv.excel):
    print (f1, f2, f3)

# b) read from a string(unicode) variable
print 'reading from a list of strings:'
reader = csv.reader(data.split('\n'), dialect=csv.excel)
for (f1, f2, f3) in reader:
    print (f1, f2, f3)

da.csv:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert
furgão
fonte
Eu me pergunto em qual versão do python isso funcionaria? Recebo erros com 2.7 e 3.5. "ValueError: valores insuficientes para descompactar (esperado 3, obtido 1)"
eis
@eis: Posso imaginar que no seu sistema a vírgula não seja um delimitador padrão. Tente adicionar em delimiter=','vez de dialect=csv.excel.
van
1

É importante notar que, se nada funcionou para você, pode ter esquecido de escapar do seu caminho.
Por exemplo, este código:

f = open("C:\Some\Path\To\file.csv")

Resultaria em um erro:

SyntaxError: (erro de unicode) codec 'unicodeescape' não pode decodificar bytes na posição 2-3: truncado \ UXXXXXXXX escape

Para corrigir, basta fazer:

f = open("C:\\Some\\Path\\To\\file.csv")
OfirD
fonte
0

Olhando para a Latin-1tabela Unicode , vejo o código de caractere 00E9" LATIN SMALL LETTER E WITH AGUTE ". Este é o caractere acentuado em seus dados de amostra. Um teste simples Pythonmostra que a UTF-8codificação para esse caractere é diferente da UTF-16codificação (quase ) unicode .

>>> u'\u00e9'
u'\xe9'
>>> u'\u00e9'.encode('utf-8')
'\xc3\xa9'
>>> 

Eu sugiro que você tente encode("UTF-8")os dados Unicode antes de chamar o especial unicode_csv_reader(). A simples leitura dos dados de um arquivo pode ocultar a codificação, portanto, verifique os valores reais dos caracteres.

gimel
fonte
0

Tive o mesmo problema em outro servidor, mas percebi que os locais estão bagunçados.

export LC_ALL="en_US.UTF-8"

consertou o problema

Piotr Pęczek
fonte