Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 305 – CSV File API

Autor:
Kevin Altis <altis at semi-retired.com>, Dave Cole <djc at object-craft.com.au>, Andrew McNamara <andrewm at object-craft.com.au>, Skip Montanaro <skip at pobox.com>, Cliff Wells <LogiplexSoftware at earthlink.net>
Discussions-To:
Csv Liste
Status:
Final
Typ:
Standards Track
Erstellt:
26-Jan-2003
Python-Version:
2.3
Post-History:
31-Jan-2003, 13-Feb-2003

Inhaltsverzeichnis

Zusammenfassung

Das Comma Separated Values (CSV)-Dateiformat ist das gebräuchlichste Import- und Exportformat für Tabellenkalkulationen und Datenbanken. Obwohl viele CSV-Dateien einfach zu parsen sind, ist das Format nicht formal durch eine stabile Spezifikation definiert und ist subtil genug, dass das Parsen von Zeilen einer CSV-Datei mit etwas wie line.split(",") letztendlich fehlschlagen wird. Dieses PEP definiert eine API für das Lesen und Schreiben von CSV-Dateien. Es wird von einem entsprechenden Modul begleitet, das die API implementiert.

To Do (Hinweise für Interessierte und Ehrgeizige)

Anwendungsbereich

Dieses PEP befasst sich mit einer Sache, die es gut machen soll: dem Parsen tabellarischer Daten, die verschiedene Feldtrenner, Anführungszeichen, Escape-Mechanismen für Anführungszeichen und Zeilenenden verwenden können. Die Autoren beabsichtigen, dass das vorgeschlagene Modul dieses eine Parsing-Problem effizient löst. Die Autoren beabsichtigen nicht, einige dieser verwandten Themen anzugehen

  • Dateninterpretation (soll ein Feld, das den String „10“ enthält, ein String, ein Float oder ein Integer sein? Ist es eine Zahl in Basis 10, Basis 16 oder Basis 2? Sind Zahlen in Anführungszeichen Zahlen oder Strings?)
  • lokale Datendarstellung (soll die Zahl 1.23 als „1.23“ oder „1,23“ oder „1 23“ geschrieben werden?) – dies könnte eventuell angegangen werden.
  • feste breite tabellarische Daten – können bereits zuverlässig geparst werden.

Begründung

Oft sind CSV-Dateien so einfach formatiert, dass Sie sie zeilenweise lesen und auf den Kommas aufteilen können, die die Felder trennen. Dies gilt insbesondere, wenn alle gelesenen Daten numerisch sind. Dieser Ansatz funktioniert vielleicht eine Weile, schlägt dann aber fehl, wenn jemand etwas Unerwartetes in die Daten einfügt, wie z. B. ein Komma. Wenn Sie sich mit dem Problem befassen, kommen Sie möglicherweise zu dem Schluss, dass Sie das Problem mit regulären Ausdrücken lösen können. Dies funktioniert eine Weile und bricht dann eines Tages auf mysteriöse Weise. Das Problem wächst, also graben Sie tiefer und erkennen schließlich, dass Sie einen speziell angefertigten Parser für das Format benötigen.

CSV-Formate sind nicht gut definiert und verschiedene Implementierungen haben eine Reihe subtiler Sonderfälle. Es wurde vorgeschlagen, dass das „V“ in dem Akronym für „Vague“ (vage) statt für „Values“ (Werte) steht. Unterschiedliche Trennzeichen und Anführungszeichen sind nur der Anfang. Einige Programme generieren nach jedem Trennzeichen Leerzeichen, die nicht Teil des folgenden Feldes sind. Andere setzen Anführungszeichen um eingebettete Anführungszeichen, indem sie sie verdoppeln, andere, indem sie ihnen ein Escape-Zeichen voranstellen. Die Liste der seltsamen Vorgehensweisen kann endlos erscheinen.

All diese Variabilität bedeutet, dass es für Programmierer schwierig ist, CSV-Dateien aus vielen Quellen zuverlässig zu parsen oder CSV-Dateien zu generieren, die für die Eingabe in bestimmte externe Programme bestimmt sind, ohne ein gründliches Verständnis dieser Quellen und Programme. Dieses PEP und die begleitende Software versuchen, den Prozess weniger fehleranfällig zu machen.

Bestehende Module

Dieses Problem wurde bereits angegangen. Mindestens drei Module, die derzeit in der Python-Community verfügbar sind, ermöglichen Programmierern das Lesen und Schreiben von CSV-Dateien

  • Object Crafts CSV-Modul [2]
  • Cliff Wells Python-DSV-Modul [3]
  • Laurence Tratts ASV-Modul [4]

Jedes hat eine andere API, was es für Programmierer etwas schwierig macht, zwischen ihnen zu wechseln. Ein größeres Problem mag sein, dass sie einige der CSV-Sonderfälle unterschiedlich interpretieren, so dass der Programmierer, selbst nachdem er die Unterschiede zwischen den verschiedenen Modul-APIs überwunden hat, auch mit semantischen Unterschieden zwischen den Paketen umgehen muss.

Modulschnittstelle

Dieses PEP unterstützt drei grundlegende APIs: eine zum Lesen und Parsen von CSV-Dateien, eine zum Schreiben und eine zum Identifizieren verschiedener CSV-Dialekte für die Reader und Writer.

Lesen von CSV-Dateien

CSV-Reader werden mit der Reader-Factory-Funktion erstellt

obj = reader(iterable [, dialect='excel']
             [optional keyword args])

Ein Reader-Objekt ist ein Iterator, der ein iterierbares Objekt, das Zeilen zurückgibt, als einziges erforderliches Argument nimmt. Wenn es einen Binärmodus unterstützt (Dateiobjekte tun dies), muss das iterierbare Argument an die Reader-Funktion im Binärmodus geöffnet worden sein. Dies gibt dem Reader-Objekt die volle Kontrolle über die Interpretation des Dateiinhalts. Der optionale Dialekt-Parameter wird unten diskutiert. Die Reader-Funktion akzeptiert auch mehrere optionale Schlüsselwortargumente, die spezifische Formateinstellungen für den Parser definieren (siehe Abschnitt „Formatierungsparameter“). Reader werden typischerweise wie folgt verwendet

csvreader = csv.reader(file("some.csv"))
for row in csvreader:
    process(row)

Jede Zeile, die von einem Reader-Objekt zurückgegeben wird, ist eine Liste von Strings oder Unicode-Objekten.

Wenn sowohl ein Dialekt-Parameter als auch einzelne Formatierungsparameter an den Konstruktor übergeben werden, wird zuerst der Dialekt auf Formatierungsparameter abgefragt, dann werden einzelne Formatierungsparameter untersucht.

Schreiben von CSV-Dateien

Die Erstellung von Writern ist ähnlich

obj = writer(fileobj [, dialect='excel'],
             [optional keyword args])

Ein Writer-Objekt ist ein Wrapper um ein schreibbares Dateiobjekt, das im Binärmodus geöffnet ist (falls eine solche Unterscheidung getroffen wird). Es akzeptiert dieselben optionalen Schlüsselwortparameter wie der Reader-Konstruktor.

Writer werden typischerweise wie folgt verwendet

csvwriter = csv.writer(file("some.csv", "w"))
for row in someiterable:
    csvwriter.writerow(row)

Um eine Reihe von Feldnamen als erste Zeile der CSV-Datei zu generieren, muss der Programmierer sie explizit schreiben, z. B.

csvwriter = csv.writer(file("some.csv", "w"), fieldnames=names)
csvwriter.write(names)
for row in someiterable:
    csvwriter.write(row)

oder dafür sorgen, dass sie die erste Zeile im geschriebenen iterierbaren Objekt sind.

Verwaltung unterschiedlicher Dialekte

Da CSV ein eher schlecht definiertes Format ist, gibt es viele Möglichkeiten, wie eine CSV-Datei unterschiedlich sein kann und dennoch genau dieselben Daten enthält. Viele Werkzeuge, die tabellarische Daten importieren oder exportieren können, erlauben dem Benutzer, das Feldtrennzeichen, das Anführungszeichen, den Zeilenabschluss und andere Merkmale der Datei anzugeben. Diese können ziemlich leicht ermittelt werden, sind aber immer noch ärgerlich herauszufinden und ergeben ziemlich lange Funktionsaufrufe, wenn sie einzeln angegeben werden.

Um die Schwierigkeit, eine Reihe von Formatierungsparametern herauszufinden und anzugeben, zu minimieren, unterstützen Reader- und Writer-Objekte ein Dialekt-Argument, das nur ein praktischer Griff für eine Gruppe dieser niedrigeren Parameter ist. Wenn ein Dialekt als String angegeben wird, identifiziert er einen der dem Modul bekannten Dialekte über seine Registrierungsfunktionen, andernfalls muss es eine Instanz der Dialekt-Klasse sein, wie unten beschrieben.

Dialekte werden im Allgemeinen nach Anwendungen oder Organisationen benannt, die spezifische Satz von Formatbeschränkungen definieren. Zwei Dialekte sind zum Zeitpunkt der Erstellung dieses Dokuments im Modul definiert: „excel“, das die Standardformatbeschränkungen für den CSV-Dateiexport von Excel 97 und Excel 2000 beschreibt, und „excel-tab“, das dasselbe wie „excel“ ist, aber ein ASCII-TAB-Zeichen als Feldtrenner angibt.

Dialekte werden als reine Attributklassen implementiert, um es Benutzern zu ermöglichen, Varianten-Dialekte durch Unterklasse zu konstruieren. Der „excel“-Dialekt ist eine Unterklasse von Dialect und ist wie folgt definiert

class Dialect:
    # placeholders
    delimiter = None
    quotechar = None
    escapechar = None
    doublequote = None
    skipinitialspace = None
    lineterminator = None
    quoting = None

class excel(Dialect):
    delimiter = ','
    quotechar = '"'
    doublequote = True
    skipinitialspace = False
    lineterminator = '\r\n'
    quoting = QUOTE_MINIMAL

Der „excel-tab“-Dialekt ist definiert als

class exceltsv(excel):
    delimiter = '\t'

(Eine Beschreibung der einzelnen Formatierungsparameter finden Sie im Abschnitt „Formatierungsparameter“.)

Um String-Referenzen auf bestimmte Dialekte zu ermöglichen, definiert das Modul mehrere Funktionen

dialect = get_dialect(name)
names = list_dialects()
register_dialect(name, dialect)
unregister_dialect(name)

get_dialect() gibt die dem angegebenen Namen zugeordnete Dialektinstanz zurück. list_dialects() gibt eine Liste aller registrierten Dialektnamen zurück. register_dialects() ordnet einen String-Namen einer Dialektklasse zu. unregister_dialect() löscht eine Namens-/Dialektzuordnung.

Formatierungsparameter

Zusätzlich zum Dialekt-Argument akzeptieren sowohl der Reader- als auch der Writer-Konstruktor mehrere spezifische Formatierungsparameter, die als Schlüsselwortparameter angegeben werden. Die verstandenen Formatierungsparameter sind

  • quotechar gibt ein einzelnes Zeichen als Anführungszeichen an. Es ist standardmäßig „"“. Das Setzen auf None hat den gleichen Effekt wie das Setzen von quoting auf csv.QUOTE_NONE.
  • delimiter gibt ein einzelnes Zeichen als Feldtrenner an. Es ist standardmäßig „,“.
  • escapechar gibt ein einzelnes Zeichen an, das zum Escapen des Trennzeichens verwendet wird, wenn quotechar auf None gesetzt ist.
  • skipinitialspace gibt an, wie Leerzeichen interpretiert werden sollen, die einem Trennzeichen unmittelbar folgen. Es ist standardmäßig False, was bedeutet, dass Leerzeichen, die einem Trennzeichen unmittelbar folgen, Teil des folgenden Feldes sind.
  • lineterminator gibt die Zeichensequenz an, die Zeilen beenden soll.
  • quoting steuert, wann Anführungszeichen vom Writer generiert werden sollen. Es kann einen der folgenden Modulkonstanten annehmen
    • csv.QUOTE_MINIMAL bedeutet nur, wenn erforderlich, z. B. wenn ein Feld entweder das Anführungszeichen oder den Trennzeichen enthält
    • csv.QUOTE_ALL bedeutet, dass Felder immer in Anführungszeichen gesetzt werden.
    • csv.QUOTE_NONNUMERIC bedeutet, dass Nicht-Numerische Felder immer in Anführungszeichen gesetzt werden.
    • csv.QUOTE_NONE bedeutet, dass Felder nie in Anführungszeichen gesetzt werden.
  • doublequote steuert die Handhabung von Anführungszeichen innerhalb von Feldern. Wenn True, werden zwei aufeinanderfolgende Anführungszeichen beim Lesen als eines interpretiert, und beim Schreiben wird jedes Anführungszeichen als zwei Anführungszeichen geschrieben.

Bei der Verarbeitung einer Dialekteinstellung und eines oder mehrerer anderer optionaler Parameter wird der Dialekt-Parameter vor den individuellen Formatierungsparametern verarbeitet. Dies erleichtert die Auswahl eines Dialekts und das Überschreiben einer oder mehrerer Einstellungen, ohne eine neue Dialektklasse definieren zu müssen. Wenn beispielsweise eine CSV-Datei von Excel 2000 unter Verwendung von einfachen Anführungszeichen als Anführungszeichen und einem Doppelpunkt als Trennzeichen generiert wurde, könnten Sie einen Reader wie folgt erstellen

csvreader = csv.reader(file("some.csv"), dialect="excel",
                       quotechar="'", delimiter=':')

Andere Details, wie Excel CSV-Dateien generiert, würden automatisch aufgrund der Referenz auf den „excel“-Dialekt behandelt.

Reader-Objekte

Reader-Objekte sind iterierbare Objekte, deren next()-Methode eine Sequenz von Strings zurückgibt, ein String pro Feld in der Zeile.

Writer-Objekte

Writer-Objekte haben zwei Methoden: writerow() und writerows(). Die erste akzeptiert ein iterierbares Objekt (typischerweise eine Liste) von Feldern, die in die Ausgabe geschrieben werden sollen. Die letztere akzeptiert eine Liste von iterierbaren Objekten und ruft writerow() für jede auf.

Implementierung

Es gibt eine Beispielimplementierung. [1] Ziel ist es, die im PEP beschriebene API effizient zu implementieren. Sie basiert stark auf dem Object Craft csv-Modul. [2]

Testen

Die Beispielimplementierung [1] enthält einen Satz von Testfällen.

Probleme

  1. Soll ein Parameter steuern, wie aufeinanderfolgende Trennzeichen interpretiert werden? Unser Gedanke ist „nein“. Aufeinanderfolgende Trennzeichen sollten immer ein leeres Feld bedeuten.
  2. Was ist mit Unicode? Reicht es aus, ein Dateiobjekt zu übergeben, das von codecs.open() erhalten wurde? Zum Beispiel
    csvreader = csv.reader(codecs.open("some.csv", "r", "cp1252"))
    
    csvwriter = csv.writer(codecs.open("some.csv", "w", "utf-8"))
    

    Im ersten Beispiel wird angenommen, dass Text als cp1252 kodiert ist. Soll das System aggressiv in die Umwandlung in Unicode konvertieren oder sollen Unicode-Strings nur zurückgegeben werden, wenn nötig?

    Im zweiten Beispiel kümmert sich die Datei automatisch um die Kodierung von Unicode-Strings als utf-8, bevor sie auf die Festplatte geschrieben werden.

    Hinweis: Zum Zeitpunkt der Erstellung dieses Dokuments verarbeitet das csv-Modul keine Unicode-Daten.

  3. Was ist mit alternativen Escape-Konventionen? Wenn der verwendete Dialekt einen escapechar-Parameter enthält, der nicht None ist, und der quoting-Parameter auf QUOTE_NONE gesetzt ist, werden Trennzeichen, die innerhalb von Feldern vorkommen, beim Schreiben mit dem Escape-Zeichen vorangestellt und sollen beim Lesen mit dem Escape-Zeichen vorangestellt werden.
  4. Soll es einen Modus zum „vollständigen Anführungszeichen setzen“ für das Schreiben geben? Was ist mit „vollständiges Anführungszeichen setzen, außer für numerische Werte“? Beide sind implementiert (QUOTE_ALL bzw. QUOTE_NONNUMERIC).
  5. Was ist mit Zeilenende? Wenn ich eine CSV-Datei unter einem Unix-System generiere, erkennt Excel dann die nur LF enthaltenden Zeilenabschlüsse korrekt? Dateien müssen zum Lesen oder Schreiben entsprechend im Binärmodus geöffnet werden. Geben Sie die lineterminator-Sequenz als '\r\n' an. Die resultierende Datei wird korrekt geschrieben.
  6. Gibt es eine Option, um Dictionaries aus dem Reader zu generieren und Dictionaries vom Writer zu akzeptieren? Siehe die Klassen DictReader und DictWriter in csv.py.
  7. Sind Anführungszeichen und Trennzeichen auf einzelne Zeichen beschränkt? Vorerst ja.
  8. Wie sollen Zeilen unterschiedlicher Länge behandelt werden? Die Interpretation der Daten ist Aufgabe der Anwendung. Auf dieser Ebene gibt es keine „kurze Zeile“ oder „lange Zeile“.

Referenzen

Es gibt viele Verweise auf andere CSV-bezogene Projekte im Web. Einige sind hier aufgeführt.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0305.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT