Leer un archivo CSV UTF8 con Python

93

Estoy intentando leer un archivo CSV con caracteres acentuados con Python (solo caracteres franceses y / o españoles). Basado en la documentación de Python 2.5 para el csvreader ( http://docs.python.org/library/csv.html ), se me ocurrió el siguiente código para leer el archivo CSV ya que csvreader solo admite 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:
        ...

A continuación se muestra un extracto del archivo CSV que estoy tratando de leer:

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

Aunque intento codificar / decodificar en UTF-8, sigo recibiendo la siguiente excepción:

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)

¿Cómo puedo solucionar esto?

Martín
fuente
Martin, si está cerca, ¿consideraría cambiar la respuesta aceptada de la única respuesta de Python 2 de Martelli?
Antti Haapala

Respuestas:

113

El .encodemétodo se aplica a una cadena Unicode para hacer una cadena de bytes; pero lo estás llamando en una cadena de bytes en su lugar ... ¡al revés! Mire el codecsmódulo en la biblioteca estándar y, codecs.openen particular, para obtener mejores soluciones generales para leer archivos de texto codificados en UTF-8. Sin embargo, para el csvmódulo en particular, debe pasar datos utf-8, y eso es lo que ya está obteniendo, por lo que su código puede ser mucho más simple:

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 

PD: si resulta que sus datos de entrada NO están en utf-8, pero por ejemplo en ISO-8859-1, entonces necesita una "transcodificación" (si está interesado en usar utf-8 a csvnivel de módulo) , del formulario line.decode('whateverweirdcodec').encode('utf-8'), pero probablemente pueda usar el nombre de su codificación existente en la yieldlínea de mi código anterior, en lugar de 'utf-8', como en csvrealidad va a estar bien con cadenas de bytes codificadas con ISO-8859- *.

Alex Martelli
fuente
4
¿Significa esto que el ejemplo en los documentos de Python (donde OP copiar y pegar) es incorrecto? ¿Cuál es el punto del paso de codificación adicional que hace si se rompe cuando le da un csv Unicode?
Anentropic
80

Python 2.X

Existe una biblioteca unicode-csv que debería resolver sus problemas, con el beneficio adicional de no tener que escribir ningún código nuevo relacionado con csv.

Aquí hay un ejemplo de su archivo Léame:

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

En python 3, esto es compatible con el csvmódulo integrado. Vea este ejemplo:

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

Si desea leer un archivo CSV con codificación utf-8, un enfoque minimalista que le recomiendo es usar algo como esto:

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

Con esa declaración, puede utilizar más tarde un lector de CSV para trabajar.

Nick Cuevas
fuente
2
¿Es posible que esto sea solo Python 3? Se produce un error para mí, en Python 2. No acepta el encodingenopen
zvika
@Zvika sí, en Python 3 esta solución funciona:open('file.csv', 'r', encoding="ISO8859")
luca76
También agregaría open (file_name, "rt", encoding = 'utf-8'), es decir, abrir archivo en modo "leer texto"
Jimmy Lee Jones
3

También consulte la respuesta en esta publicación: https://stackoverflow.com/a/9347871/1338557

Sugiere el uso de una biblioteca llamada ucsv.py. Reemplazo corto y simple para CSV escrito para abordar el problema de codificación (utf-8) para Python 2.7. También proporciona soporte para csv.DictReader

Editar : Agregar código de muestra que usé:

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
fuente
debe poner algunos detalles de ese enlace en su respuesta, en caso de que el enlace se rompa \
Yaje
# Downvoter: no estoy seguro de por qué pensó que no tenía ninguna utilidad. La biblioteca de ucsv funcionó bien para mí. Ayudó a resolver el error Unicde con el que había estado luchando desde hace 2 días. Si estaba buscando un código de muestra, aquí va en la edición @ Yaje- He dado algunos detalles; también el código de muestra. Y también se corrigió el enlace, que antes apuntaba a otra publicación.
Atripavan
¿Alguna razón en particular por la que está abriendo un archivo de texto como binario? 'rb' es para abrir archivos binarios.
Codeguy007
2

Usar codecs.opencomo sugirió Alex Martelli resultó ser útil para mí.

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 ...
usuario1154664
fuente
3
No funcionaría con todos los CSV, a continuación se muestra una fila csv válida: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.
Importa el csvmódulo pero no lo usa.
Christophe Roussy
1

El enlace a la página de ayuda es el mismo para python 2.6 y, hasta donde yo sé, no hubo cambios en el módulo csv desde 2.5 (además de las correcciones de errores). Aquí está el código que simplemente funciona sin codificación / decodificación (el archivo da.csv contiene los mismos datos que los datos variables ). Supongo que su archivo debe leerse correctamente sin conversiones.

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
camioneta
fuente
Me pregunto en qué versión de Python funcionaría esto. Recibo errores tanto con 2.7 como con 3.5. "ValueError: no hay suficientes valores para descomprimir (esperado 3, obtenido 1)"
eis
@eis: Puedo imaginar que en su sistema la coma no es un delimitador predeterminado. Intente agregar en delimiter=','lugar de dialect=csv.excel.
furgoneta
1

Vale la pena señalar que si nada funcionó para usted, es posible que haya olvidado escapar de su camino.
Por ejemplo, este código:

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

Resultaría en un error:

SyntaxError: (error unicode) el códec 'unicodeescape' no puede decodificar bytes en la posición 2-3: escape truncado \ UXXXXXXXX

Para solucionarlo, simplemente haz lo siguiente:

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

Mirando la Latin-1tabla Unicode , veo el código de carácter 00E9" LETRA E MINÚSCULA LATINA CON AGUDO ". Este es el carácter acentuado en sus datos de muestra. Una prueba simple Pythonmuestra que la UTF-8codificación de este carácter es diferente de la UTF-16codificación unicode (casi ).

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

Le sugiero que intente con encode("UTF-8")los datos Unicode antes de llamar al especial unicode_csv_reader(). Simplemente leer los datos de un archivo puede ocultar la codificación, así que verifique los valores reales de los caracteres.

gimel
fuente
0

Tuve el mismo problema en otro servidor, pero me di cuenta de que las configuraciones regionales están desordenadas.

export LC_ALL="en_US.UTF-8"

solucionó el problema

Piotr Pęczek
fuente