it-swarm.com.de

Kann ich eine CSV-Datei importieren und den Begrenzer automatisch ableiten?

Ich möchte zwei Arten von CSV-Dateien importieren, einige verwenden ";" für Trennzeichen und andere verwenden Sie ",". Bisher habe ich zwischen den nächsten beiden Zeilen gewechselt:

reader=csv.reader(f,delimiter=';')

oder

reader=csv.reader(f,delimiter=',')

Ist es möglich, das Trennzeichen nicht anzugeben und das Programm nach dem richtigen Trennzeichen suchen zu lassen?

Die folgenden Lösungen (Blender und Sharth) scheinen für durch Kommas getrennte Dateien (mit Libroffice generiert) gut zu funktionieren, nicht jedoch für durch Semikolon getrennte Dateien (mit MS Office generiert). Hier sind die ersten Zeilen einer durch Semikolons getrennten Datei:

ReleveAnnee;ReleveMois;NoOrdre;TitreRMC;AdopCSRegleVote;AdopCSAbs;AdoptCSContre;NoCELEX;ProposAnnee;ProposChrono;ProposOrigine;NoUniqueAnnee;NoUniqueType;NoUniqueChrono;PropoSplittee;Suite2LecturePE;Council PATH;Notes
1999;1;1;1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC;U;;;31999D0083;1998;577;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
1999;1;2;1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes;U;;;31999D0081;1998;184;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
43
rom

Um das Problem zu lösen, habe ich eine Funktion erstellt, die die erste Zeile einer Datei (Header) liest und den Begrenzer erkennt.

def detectDelimiter(csvFile):
    with open(csvFile, 'r') as myCsvfile:
        header=myCsvfile.readline()
        if header.find(";")!=-1:
            return ";"
        if header.find(",")!=-1:
            return ","
    #default delimiter (MS Office export)
    return ";"
7
rom

Das csv Modul scheint die Verwendung des csv sniffer für dieses Problem zu empfehlen.

Sie geben das folgende Beispiel, das ich für Ihren Fall angepasst habe.

with open('example.csv', 'rb') as csvfile:  # python 3: 'r',newline=""
    dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=";,")
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    # ... process CSV file contents here ...

Probieren wir es aus.

[9:13am][[email protected] /tmp] cat example 
#!/usr/bin/env python
import csv

def parse(filename):
    with open(filename, 'rb') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(), delimiters=';,')
        csvfile.seek(0)
        reader = csv.reader(csvfile, dialect)

        for line in reader:
            print line

def main():
    print 'Comma Version:'
    parse('comma_separated.csv')

    print
    print 'Semicolon Version:'
    parse('semicolon_separated.csv')

    print
    print 'An example from the question (kingdom.csv)'
    parse('kingdom.csv')

if __== '__main__':
    main()

Und unsere Beispieleingaben

[9:13am][[email protected] /tmp] cat comma_separated.csv 
test,box,foo
round,the,bend

[9:13am][[email protected] /tmp] cat semicolon_separated.csv 
round;the;bend
who;are;you

[9:22am][[email protected] /tmp] cat kingdom.csv 
ReleveAnnee;ReleveMois;NoOrdre;TitreRMC;AdopCSRegleVote;AdopCSAbs;AdoptCSContre;NoCELEX;ProposAnnee;ProposChrono;ProposOrigine;NoUniqueAnnee;NoUniqueType;NoUniqueChrono;PropoSplittee;Suite2LecturePE;Council PATH;Notes
1999;1;1;1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC;U;;;31999D0083;1998;577;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document
1999;1;2;1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes;U;;;31999D0081;1998;184;COM;NULL;CS;NULL;;;;Propos* are missing on Celex document

Und wenn wir das Beispielprogramm ausführen:

[9:14am][[email protected] /tmp] ./example 
Comma Version:
['test', 'box', 'foo']
['round', 'the', 'bend']

Semicolon Version:
['round', 'the', 'bend']
['who', 'are', 'you']

An example from the question (kingdom.csv)
['ReleveAnnee', 'ReleveMois', 'NoOrdre', 'TitreRMC', 'AdopCSRegleVote', 'AdopCSAbs', 'AdoptCSContre', 'NoCELEX', 'ProposAnnee', 'ProposChrono', 'ProposOrigine', 'NoUniqueAnnee', 'NoUniqueType', 'NoUniqueChrono', 'PropoSplittee', 'Suite2LecturePE', 'Council PATH', 'Notes']
['1999', '1', '1', '1999/83/EC: Council Decision of 18 January 1999 authorising the Kingdom of Denmark to apply or to continue to apply reductions in, or exemptions from, excise duties on certain mineral oils used for specific purposes, in accordance with the procedure provided for in Article 8(4) of Directive 92/81/EEC', 'U', '', '', '31999D0083', '1998', '577', 'COM', 'NULL', 'CS', 'NULL', '', '', '', 'Propos* are missing on Celex document']
['1999', '1', '2', '1999/81/EC: Council Decision of 18 January 1999 authorising the Kingdom of Spain to apply a measure derogating from Articles 2 and 28a(1) of the Sixth Directive (77/388/EEC) on the harmonisation of the laws of the Member States relating to turnover taxes', 'U', '', '', '31999D0081', '1998', '184', 'COM', 'NULL', 'CS', 'NULL', '', '', '', 'Propos* are missing on Celex document']

Es ist wahrscheinlich auch erwähnenswert, welche Version von python ich verwende.

[9:20am][[email protected] /tmp] python -V
Python 2.7.2
47
Bill Lynch

Bei einem Projekt, das sich sowohl mit (Komma) als auch mit | befasst (vertikaler Balken) CSV-Dateien mit Trennzeichen, die wohlgeformt sind, habe ich folgendermaßen ausprobiert (wie angegeben unter https://docs.python.org/2/library/csv.html#csv.Sniffer ) :

dialect = csv.Sniffer().sniff(csvfile.read(1024), delimiters=',|')

In einer | -begrenzten Datei wurde jedoch die Ausnahme "Begrenzer konnte nicht ermittelt werden" zurückgegeben. Es schien vernünftig zu spekulieren, dass die Sniff-Heuristik am besten funktionieren könnte, wenn jede Zeile dieselbe Anzahl von Begrenzern aufweist (ohne die in Anführungszeichen eingeschlossenen Werte). Anstatt also die ersten 1024 Bytes der Datei zu lesen, habe ich versucht, die ersten beiden Zeilen vollständig zu lesen:

temp_lines = csvfile.readline() + '\n' + csvfile.readline()
dialect = csv.Sniffer().sniff(temp_lines, delimiters=',|')

Bisher funktioniert das gut für mich.

7
Andrew Basile

Und wenn Sie DictReader verwenden, können Sie das tun:

#!/usr/bin/env python
import csv

def parse(filename):
    with open(filename, 'rb') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(), delimiters=';,')
        csvfile.seek(0)
        reader = csv.DictReader(csvfile, dialect=dialect)

        for line in reader:
            print(line['ReleveAnnee'])

Ich habe dies mit Python 3.5 und es hat so funktioniert.

6

Ich glaube nicht, dass es eine vollkommen allgemeine Lösung dafür geben kann (einer der Gründe, warum ich , als Begrenzer verwenden könnte, ist, dass einige meiner Datenfelder ; enthalten müssen ...). Eine einfache Heuristik für die Entscheidung könnte darin bestehen, einfach die erste Zeile (oder mehr) zu lesen, zu zählen, wie viele ,- und ;-Zeichen darin enthalten sind (möglicherweise ignorieren Sie diese in Anführungszeichen, wenn irgendetwas Ihre .csv-Dateien erstellt, und raten Sie, dass Einträge richtig und konsistent sind) Das häufigere der beiden ist das richtige Trennzeichen.

2
twalberg