33.6 Das Tabellenformat CSV – csv 

Ein weitverbreitetes Import- und Exportformat für Datenbanken und Tabellenkalkulationen ist das CSV-Format (CSV steht für Comma Separated Values). CSV-Dateien sind Textdateien, die zeilenweise Datensätze enthalten. Innerhalb der Datensätze sind die einzelnen Werte durch ein Trennzeichen, wie beispielsweise das Komma, voneinander getrennt, daher auch der Name.
Eine CSV-Datei, die Informationen zu Personen speichert und das Komma als Trennzeichen nutzt, könnte beispielsweise so aussehen:
vorname,nachname,geburtsdatum,wohnort,haarfarbe
Heinrich,Huhn,19.07.1980,Berlin,Braun
Rudolf,Geier,19.09.1990,Dortmund,Braun
Haken,Habicht,14.04.1959,Hamburg,Dunkelblond
Edith,Falke,13.09.1987,Köln,Schwarz
Rüdiger,Amsel,25.03.1988,München,Hellrot
Die erste Zeile enthält die jeweiligen Spaltenköpfe, und alle folgenden Zeilen enthalten die eigentlichen Datensätze.
Leider existiert kein Standard für CSV-Dateien, sodass sich beispielsweise das Trennzeichen von Programm zu Programm unterscheiden kann. Dieser Umstand erschwert es, CSV-Dateien von verschiedenen Quellen zu lesen, da immer auf das besondere Format der exportierenden Anwendung eingegangen werden muss.
Um trotzdem mit CSV-Dateien der verschiedensten Formate umgehen zu können, stellt Python das Modul csv zur Verfügung. Das csv-Modul implementiert reader- und writer-Klassen, die den Lese- bzw. Schreibzugriff auf CSV-Daten kapseln. Mithilfe sogenannter Dialekte kann dabei das Format der Datei angegeben werden. Standardmäßig gibt es vordefinierte Dialekte für die CSV-Dateien, die von Microsoft Excel generiert werden. Außerdem stellt das Modul eine Klasse namens Sniffer (dt. »Schnüffler«) bereit, die den Dialekt einer Datei erraten kann.
Eine Liste aller definierten Dialekte erhalten Sie mit csv.list_dialects:
>>> import csv
>>> csv.list_dialects()
['excel', 'excel-tab', 'unix']
33.6.1 reader-Objekte – Daten aus einer CSV-Datei lesen 

Mithilfe von reader-Objekten können CSV-Dateien gelesen werden. Der Konstruktor sieht dabei wie im Folgenden beschrieben aus.
csv.reader(csvfile, [dialect], {**fmtparam})
Der Parameter csvfile muss eine Referenz auf ein für den Lesezugriff geöffnetes Dateiobjekt sein, aus dem die Daten gelesen werden sollen.
Mit dialect können Sie angeben, in welchem Format die zu lesende Datei geschrieben wurde. Dazu übergeben Sie als dialect einen String, der in der Liste enthalten ist, die csv.list_dialects zurückgibt. Alternativ geben Sie eine Instanz der Klasse Dialect an, die wir in Abschnitt 33.6.2 besprechen werden. Standardmäßig wird der Wert "excel" für dialect verwendet, wobei die damit codierten Dateien das Komma als Trennzeichen verwenden.
Der Platzhalter **fmtparam steht nicht für einen einzelnen Parameter, sondern für Schlüsselwortparameter, die übergeben werden können, um den Dialekt ohne Umweg über die Dialect-Klasse festzulegen. Ein Beispiel, bei dem wir auf diese Weise das Semikolon als Trennzeichen zwischen den einzelnen Werten festlegen, sieht folgendermaßen aus:
>>> reader = csv.reader(open("datei.csv"), delimiter=";")
Eine Übersicht über Dialekte und mögliche Werte für fmtparam finden Sie in Abschnitt 33.6.2.
Die reader-Instanzen implementieren das Iterator-Protokoll und lassen sich deshalb zum Beispiel komfortabel mit einer for-Schleife verarbeiten. Im folgenden Beispiel lesen wir die CSV-Datei mit den Personen:
>>> reader = csv.reader(open("namen.csv"))
>>> for row in reader:
... print(row)
['vorname', 'nachname', 'geburtsdatum', 'wohnort', 'haarfarbe']
['Heinrich', 'Huhn', '19.07.1980', 'Berlin', 'Braun']
['Rudolf', 'Geier', '19.09.1990', 'Dortmund', 'Braun']
['Haken', 'Habicht', '14.04.1959', 'Hamburg', 'Dunkelblond']
['Edith', 'Falke', '13.09.1987', 'Köln', 'Schwarz']
['Rüdiger', 'Amsel', '25.03.1988', 'München', 'Hellrot']
Wie Sie sehen, gibt uns der reader für jede Zeile eine Liste mit den Werten der einzelnen Spalten zurück. Wichtig ist dabei, dass die Spaltenwerte immer als Strings zurückgegeben werden.
Neben dem Standard-reader, der Listen zurückgibt, existiert noch der sogenannte DictReader, der für jede Zeile ein geordnetes Dictionary (OrderedDict) erzeugt, das den Spaltenköpfen die Werte der jeweiligen Zeile zuordnet.
Unser letztes Beispiel verändert sich durch die Verwendung von DictReader wie folgt, wobei wir nur die ersten beiden Datensätze beispielhaft ausgeben:
>>> reader = csv.DictReader(open("namen.csv"))
>>> for row in reader:
... print(row)
OrderedDict([('vorname', 'Heinrich'), ('nachname', 'Huhn'), ('geburtsdatum', '19.07.1980'), ('wohnort', 'Berlin'), ('haarfarbe', 'Braun')])
OrderedDict([('vorname', 'Rudolf'), ('nachname', 'Geier'), ('geburtsdatum', '19.09.1990'), ('wohnort', 'Dortmund'), ('haarfarbe', 'Braun')])
[…]
writer-Objekte – Daten in CSV-Dateien schreiben
Der Konstruktor der writer-Klasse erwartet die gleichen Parameter wie der Konstruktor der reader-Klasse, mit der Ausnahme, dass das für csvfile übergebene Dateiobjekt für den Schreibzugriff geöffnet worden sein muss.
csv.writer(csvfile, [dialect], {**fmtparam})
Das resultierende writer-Objekt hat die beiden Methoden writerow und writerows, mit denen sich einzelne bzw. mehrere Zeilen auf einmal in die CSV-Datei schreiben lassen:
>>> writer = csv.writer(open("autos.csv", "w"))
>>> writer.writerow(["marke", "modell", "leistung_in_ps"])
29
>>> daten = (
... ["Volvo", "P245", "130"], ["Ford", "Focus", "90"],
... ["Mercedes", "CLK", "250"], ["Audi", "A6", "350"],
... )
>>> writer.writerows(daten)
In dem Beispiel erzeugen wir eine neue CSV-Datei mit dem Namen "autos.csv". Mit der writerow-Methode schreiben wir die Spaltenköpfe in die erste Zeile der neuen Datei und mit writerows anschließend vier Beispieldatensätze.
Analog zur DictReader-Klasse existiert auch eine DictWriter-Klasse, die sich fast genauso wie die normale writer-Klasse erzeugen lässt, außer dass Sie neben dem Dateiobjekt noch eine Liste mit den Spaltenköpfen übergeben müssen. Für ihre writerow- und writerows-Methoden erwarten DictWriter-Instanzen Dictionarys als Parameter, und mit der Methode writeheader werden die Spaltenköpfe in die CSV-Datei geschrieben. Das folgende Beispiel erzeugt die gleiche CSV-Datei wie das letzte:
>>> writer = csv.DictWriter(open("autos.csv", "w"),
... ["marke", "modell", "leistung_in_ps"])
>>> writer.writeheader()
>>> daten = ({"marke" : "Volvo", "modell" : "P245",
... "leistung_in_ps" : "130"},
... {"marke" : "Ford", "modell" : "Focus",
... "leistung_in_ps" : "90"},
... {"marke" : "Mercedes", "modell" : "CLK",
... "leistung_in_ps" : "250"},
... {"marke" : "Audi", "modell" : "A6",
... "leistung_in_ps" : "350"})
>>> writer.writerows(daten)
33.6.2 Dialect-Objekte – eigene Dialekte verwenden 

Die Instanzen der Klasse csv.Dialect dienen dazu, den Aufbau von CSV-Dateien zu beschreiben. Sie sollten Dialect-Objekte nicht direkt erzeugen, sondern stattdessen die Funktion csv.register_dialect verwenden. Mit register_dialect erzeugen Sie einen neuen Dialekt und versehen ihn mit einem Namen. Dieser Name kann dann später als Parameter an die Konstruktoren der reader- und writer-Klassen übergeben werden. Außerdem ist jeder registrierte Name in der von csv.get_dialects zurückgegebenen Liste enthalten.
Die Funktion register_dialect hat die im Folgenden beschriebene Schnittstelle.
csv.register_dialect(name, [dialect], {**fmtparam})
Der Parameter name muss dabei ein String sein, der den neuen Dialekt identifiziert. Mit dialect kann ein bereits bestehendes Dialect-Objekt übergeben werden, das dann mit dem entsprechenden Namen verknüpft wird.
Am wichtigsten ist der Platzhalter **fmtparam, der für eine Reihe optionaler Schlüsselwortparameter steht, die den neuen Dialekt beschreiben. Es sind die in Tabelle 33.11 aufgeführten Parameter erlaubt:
Name | Bedeutung |
---|---|
delimiter | Trennzeichen zwischen den Spaltenwerten. Der Standardwert ist das Komma ",". |
quotechar | Ein Zeichen, um Felder zu umschließen, die besondere Zeichen wie das Trennzeichen oder den Zeilenumbruch enthalten. Der Standardwert sind die doppelten Anführungszeichen '"'. |
doublequote |
Ein boolescher Wert, der angibt, wie das für quotechar angegebene Zeichen innerhalb von Feldern selbst maskiert werden soll. Hat doublequote den Wert True, wird quotechar zweimal hintereinander eingefügt. Ist der Wert von doublequote False, wird stattdessen das für escapechar angegebene Zeichen vor quotechar geschrieben. Standardmäßig hat doublequote den Wert True. |
escapechar |
Ein Zeichen, das benutzt wird, um das Trennzeichen innerhalb von Spaltenwerten zu maskieren, sofern quoting den Wert QUOTE_NONE hat. Bei einem doublequote-Wert von False wird escapechar außerdem für die Maskierung quotechar verwendet. Standardmäßig ist die Maskierung deaktiviert, und escapechar hat den Wert None. |
lineterminator |
Zeichen, das zum Trennen der Zeilen benutzt wird. Standardmäßig ist es auf "\r\n" gesetzt. Bitte beachten Sie, dass diese Einstellung nur den Writer betrifft. Alle reader-Objekte bleiben von der lineterminator-Einstellung unbeeinflusst und verwenden immer "\r", "\n" oder die Kombination aus beiden als Zeilentrennzeichen. |
quoting |
Gibt an, ob und wann Spaltenwerte mit quotechar umschlossen werden sollen. Gültige Werte sind:
|
skipinitialspace |
Ein boolescher Wert, der angibt, wie mit führenden Whitespaces in einem Spaltenwert verfahren werden soll. Eine Einstellung auf True bewirkt, dass alle führenden Whitespaces ignoriert werden; bei einem Wert von False wird der komplette Spalteninhalt gelesen und zurückgegeben. Der Standardwert ist False. |
Tabelle 33.11 Schlüsselwortparameter für register_dialect
Wir wollen als Beispiel einen neuen Dialekt namens "mein_dialekt" registrieren, der als Trennzeichen den Tabulator verwendet und alle Felder mit Anführungszeichen umschließt:
>>> csv.register_dialect("mein_dialekt", delimiter="\t",
... quoting=csv.QUOTE_ALL)
Diesen neuen Dialekt können wir nun dem Konstruktor unserer reader- und writer-Klassen übergeben und auf diese Weise unsere eigenen CSV-Dateien schreiben und lesen.
Dialekte automatisch ermitteln
Das Modul csv stellt die Klasse csv.Sniffer bereit, mit der sich der Dialekt einer CSV-Datei aus einem Auszug automatisch erzeugen lässt. Im folgenden Beispiel verwenden wir csv.Sniffer, um den Dialekt der Datei "autos.csv" aus den vorangegangenen Beispielen zu ermitteln.
>>> sample = open("autos.csv").read(1024)
>>> dialect = csv.Sniffer().sniff(sample)
>>> dialect.delimiter
','
Das Programm hat also das Trennzeichen korrekt als Komma identifiziert. Mit dem so erzeugten Dialekt kann es nun die CSV-Datei einlesen.
>>> reader = csv.reader(open("autos.csv"), dialect)
>>> for row in reader:
... print(row)
['marke', 'modell', 'leistung_in_ps']
['Volvo', 'P245', '130']
['Ford', 'Focus', '90']
['Mercedes', 'CLK', '250']
['Audi', 'A6', '350']
Die Klasse csv.Sniffer besitzt außerdem eine Methode has_header, die ermittelt, ob die erste Zeile der Datei Spaltenköpfe enthält oder nicht. In unserem Beispiel liefert sie den Wert True zurück.
>>> csv.Sniffer().has_header(sample)
True
[»] Hinweis
Die Klasse csv.Sniffer verwendet Heuristiken, um den Dialekt zu ermitteln und zu prüfen, ob Spaltenköpfe in der ersten Zeile der Datei vorhanden sind. Diese Heuristiken können fehlschlagen, sodass Sie die zurückgegebenen Werte als qualifizierte Vermutung und nicht als sichere Aussagen betrachten sollten.