Lesen einer UTF8-CSV-Datei mit Python

93

Ich versuche, eine CSV-Datei mit Akzentzeichen mit Python zu lesen (nur französische und / oder spanische Zeichen). Basierend auf der Python 2.5-Dokumentation für den csvreader ( http://docs.python.org/library/csv.html ) habe ich den folgenden Code zum Lesen der CSV-Datei entwickelt, da der csvreader nur ASCII unterstützt.

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

Unten finden Sie einen Auszug aus der CSV-Datei, die ich zu lesen versuche:

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

Obwohl ich versuche, in UTF-8 zu codieren / decodieren, erhalte ich immer noch die folgende Ausnahme:

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)

Wie behebe ich das?

Martin
quelle
Martin, wenn Sie in der Nähe sind, würden Sie in Betracht ziehen, die akzeptierte Antwort von Martellis Python 2-Antwort zu ändern.
Antti Haapala

Antworten:

113

Die .encodeMethode wird auf eine Unicode-Zeichenfolge angewendet, um eine Byte-Zeichenfolge zu erstellen. aber du rufst es stattdessen auf einer Byte-Zeichenfolge auf ... falsch herum! Schaue auf diecodecs Modul in der Standardbibliothek an und codecs.opensuchen Sie insbesondere nach besseren allgemeinen Lösungen zum Lesen von UTF-8-codierten Textdateien. csvInsbesondere für das Modul müssen Sie jedoch utf-8-Daten übergeben, und das erhalten Sie bereits, damit Ihr Code viel einfacher sein kann:

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: Wenn sich herausstellt, dass Ihre Eingabedaten NICHT in utf-8, sondern z. B. in ISO-8859-1 vorliegen, benötigen Sie eine "Transcodierung" (wenn Sie utf-8 auf csvModulebene verwenden möchten ). , der Form line.decode('whateverweirdcodec').encode('utf-8')- aber wahrscheinlich können Sie einfach den Namen Ihrer vorhandenen Codierung in der yieldZeile in meinem Code oben verwenden, anstatt 'utf-8', wie csves mit ISO-8859- * codierten Bytestrings eigentlich in Ordnung sein wird.

Alex Martelli
quelle
4
Bedeutet dies, dass das Beispiel in den Python-Dokumenten (wo OP kopiert und eingefügt wurde) falsch ist? Was ist der Sinn des zusätzlichen Codierungsschritts, der ausgeführt wird, wenn er unterbrochen wird, wenn Sie ihm eine Unicode-CSV geben?
Anentropic
80

Python 2.X.

Es gibt eine Unicode-CSV- Bibliothek, die Ihre Probleme lösen sollte, mit dem zusätzlichen Vorteil, dass Sie nicht navigieren müssen, um neuen CSV-bezogenen Code zu schreiben.

Hier ist ein Beispiel aus ihrer 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.

In Python 3 wird dies vom integrierten csvModul sofort unterstützt. Siehe dieses Beispiel:

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

Wenn Sie eine CSV-Datei mit der Codierung utf-8 lesen möchten, empfehle ich Ihnen einen minimalistischen Ansatz: Gehen Sie wie folgt vor:

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

Mit dieser Anweisung können Sie später einen CSV-Reader zum Arbeiten verwenden.

Nick Cuevas
quelle
2
Ist es möglich, dass dies nur Python 3 ist? Es schlägt für mich in Python 2 fehl. Es akzeptiert das encodinginopen
Zvika
@ Zvika ja, in Python 3 funktioniert diese Lösung:open('file.csv', 'r', encoding="ISO8859")
Luca76
Ich würde auch open hinzufügen (Dateiname, "rt", encoding = 'utf-8'), dh offene Datei im Modus "Text lesen"
Jimmy Lee Jones
3

Überprüfen Sie auch die Antwort in diesem Beitrag: https://stackoverflow.com/a/9347871/1338557

Es wird die Verwendung der Bibliothek ucsv.py vorgeschlagen. Kurzer und einfacher Ersatz für CSV, der geschrieben wurde, um das Codierungsproblem (utf-8) für Python 2.7 zu beheben. Bietet auch Unterstützung für csv.DictReader

Bearbeiten : Hinzufügen von Beispielcode, den ich verwendet habe:

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
quelle
Sie sollten einige Details dieses Links in Ihre Antwort
einfügen
# Downvoter- Ich bin mir nicht sicher, warum du es für nutzlos gehalten hast. Die UCSV-Bibliothek hat für mich gut funktioniert. Hat geholfen, den Unicde-Fehler zu beheben, mit dem ich seit 2 Tagen zu kämpfen hatte. Wenn Sie nach einem Beispielcode gesucht haben, finden Sie ihn hier im edit @ Yaje. Ich habe einige Details angegeben. auch der Beispielcode. Und korrigierte auch den Link, der früher auf einen anderen Beitrag verwies.
Atripavan
Gibt es einen bestimmten Grund, warum Sie eine Textdatei als Binärdatei öffnen? 'rb' dient zum Öffnen von Binärdateien.
Codeguy007
2

Die Verwendung, codecs.openwie Alex Martelli vorgeschlagen hatte, erwies sich für mich als nützlich.

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
quelle
3
Es würde nicht mit allen CSVs funktionieren. Es folgt eine gültige CSV-Zeile: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.
Sie importieren das csvModul, verwenden es aber nicht.
Christophe Roussy
1

Der Link zur Hilfeseite ist der gleiche für Python 2.6 und meines Wissens gab es seit 2.5 keine Änderung im CSV-Modul (abgesehen von Fehlerkorrekturen). Hier ist der Code, der nur ohne Codierung / Decodierung funktioniert (Datei da.csv enthält dieselben Daten wie die variablen Daten ). Ich gehe davon aus, dass Ihre Datei ohne Konvertierungen korrekt gelesen werden sollte.

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
van
quelle
Ich frage mich, in welcher Python-Version würde das funktionieren? Ich erhalte Fehler mit 2.7 und 3.5. "ValueError: nicht genügend Werte zum Entpacken (erwartete 3, bekam 1)"
Eis
@eis: Ich kann mir vorstellen, dass Komma auf Ihrem System kein Standardtrennzeichen ist. Versuchen Sie , fügen delimiter=','statt dialect=csv.excel.
van
1

Wenn nichts für Sie funktioniert hat, haben Sie möglicherweise vergessen, Ihrem Weg zu entkommen.
Zum Beispiel dieser Code:

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

Würde zu einem Fehler führen:

SyntaxError: (Unicode-Fehler) Der Codec 'unicodeescape' kann keine Bytes an Position 2-3 dekodieren: abgeschnittenes \ UXXXXXXXX-Escape

Um dies zu beheben, gehen Sie einfach wie folgt vor:

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

Wenn ich mir die Latin-1Unicode-Tabelle ansehe , sehe ich den Zeichencode 00E9" LATIN SMALL LETTER E WITH ACUTE ". Dies ist das akzentuierte Zeichen in Ihren Beispieldaten. Ein einfacher Test in Pythonzeigt, dass sich die UTF-8Codierung für dieses Zeichen von der (fast UTF-16) Unicode- Codierung unterscheidet.

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

Ich schlage vor, Sie versuchen es mit encode("UTF-8")den Unicode-Daten, bevor Sie das Special aufrufen unicode_csv_reader(). Durch einfaches Lesen der Daten aus einer Datei wird möglicherweise die Codierung ausgeblendet. Überprüfen Sie daher die tatsächlichen Zeichenwerte.

Gimel
quelle
0

Hatte das gleiche Problem auf einem anderen Server, stellte jedoch fest, dass die Gebietsschemas durcheinander sind.

export LC_ALL="en_US.UTF-8"

Das Problem wurde behoben

Piotr Pęczek
quelle