Mit CSV Reader den Header nicht einlesen.

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
flambo
User
Beiträge: 10
Registriert: Mittwoch 7. März 2012, 15:05

Hallo zusammen,
Ich lese Daten aus einer CSV-Datei aus. Diese CSV-Datei hat allerdings einen zwei Zeilen langen Header, welcher ich nicht einlesen will.
Kann mir jemand sagen wie ich die ersten zwei Zeilen überspringen kann?

Besten Dank für eure Hilfe!!
deets

Indem du die beiden Zeilen ueberliest. Dazu solltest du allerdings *nicht* readlines verwenden, da du sonst dank interner Bufferung fuer den nachfolgenden CSV-reader falsche Daten bereitstellst.

Also gibt's zB folgende Moeglichkeiten:

- du liest Byteweise ein, bis du am Ende des Headers angekommen bist - einfach auf die Newline-Zeichen achten. Danach uebergibst du das file an den CSV-reader
- du liest zeilenweise mit readlines ein, aber pumpst die Zeilen nach dem Header in ein StringIO-Objekt. Das ist dann wiederum Quelle fuer den CSV-Reader. Vorteil: einfacher zu programmieren. Nachteil: speicheraufwendiger, haengt also von der Groesse der CSV-Datei ab. Im Zeitalter von GB-Speicher aber denke ich sollte das gehen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du könntest zweimal die ``next``-Methode des Iterables aufrufen, bevor Du es ``csv.reader`` übergibst:

Code: Alles auswählen

with open("foo.csv", "r") as infile:
    infile.next()
    infile.next()
    reader = csv.reader(infile)
    # irgend wie weiter...
Laut Doku sollte man zwar die ``next``-Methode eines ``File Objects`` nicht mit anderen Methoden kombinieren, aber wie ich das sehe wäre das ja auch nicht der Fall (vorausgesetzt ``csv.reader`` nutzt auch ``next`` - da es laut Doku einen Iterable akzeptiert, sollte es das wohl, oder?).
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Schaut man in die Doku, sieht man als einfaches Beispiel folgendes:

Code: Alles auswählen

>>> import csv
>>> spamReader = csv.reader(open('eggs.csv', 'rb'), delimiter=' ', quotechar='|')
>>> for row in spamReader:
Fügt man vor der letzten Zeile einfach 2x `spamReader.next()` ein, sollte man doch das gewünschte Ergebnis erhalten, oder?

Stefan
lunar

Also ich würde ja einfach "csv.reader(itertools.islice(fileobj, 2))" verwenden, um die ersten beiden Zeilen zu überspringen. Ich sehe auch nicht, warum es bei ".readline()" Puffer-Probleme geben sollte… "next()" puffert ja schließlich nur vorwärts, und "readline()" würde vorher aufgerufen werden.
deets

@lunar

es kann ganz bestimmt probleme geben, sowas ist mir schon passiert. wenn der CSV-reader ein iterable erwartet, ist dem im konkreten natuerlich nicht so - das habe ich nicht auf dem schirm gehabt.

aber generell sollte man sich nicht darauf verlassen, dass readlines/next/iterieren den dateizeiger nur bis zum jeweils naechsten newline bewegt - aus gutem grund. Wie ich gerade herausfand, macht python da schon selbst drauf aufmerksam:

Code: Alles auswählen


import tempfile

t = tempfile.mktemp()

with open(t, "wb") as outf:
    for _ in xrange(100):
        for i in xrange(32, 200):
            outf.write(chr(i))
        outf.write("\n")


with open(t, "rb") as inf:
    inf.next()
    inf.next()
    c = inf.read(1)
    print ord(c)
lass mal laufen mit python >= 2.5
lunar

@deets: Wenn man die Reihenfolge umdreht, also ".readline()" vor der Iteration ausführt, erhält man keine Warnung und eine korrekte Ausgabe. Das entspricht auch der Dokumentation, denn für ".readline()" ist kein Pufferverhalten dokumentiert, und dann kann man durchaus erwarten, dass der Dateizeiger nach "readline()" am Beginn der nächsten Zeile steht.
flambo
User
Beiträge: 10
Registriert: Mittwoch 7. März 2012, 15:05

Bei einer normalen Text-Datei hat es mit next() funktioniert. Aber ich habe eine csv-Datei und will mit den Einträgen folgendes machen:

Code: Alles auswählen

logfile = open("log.csv","a+")
csvreader2=csv.reader(logfile, delimiter='\t')
csvreader2.next()
csvreader2.next()

#Alle Einträge, welche älter als 24 Stunden sind werden in die Liste compdata eingelesen
compdata = []
timenow = time.time()
   
for rowlog in csvreader2:
    if timenow - float(rowlog[0]) < 86400:
        compdata.append( (rowlog[2],rowlog[3],rowlog[4],rowlog[5],rowlog[6],rowlog[7],rowlog[8],rowlog[9],rowlog[10],rowlog[11]) ) 
    
logfile.close()
Der erste Eintrag(rowlog[0]), welchen ich einlesen will ist ein Zeitstempel (time.time()) in einer Log-Datei. Wenn die Differenz von der aktuellen Zeit zu diesem grösser als 86400 ist will ich andere Einträge in eine Liste lesen. Die ersten zwei Zeilen im Log-File sind wie erwähnt Header-Zeilen. Wenn ich es wie im Skript oben mit csvreader2.next() versuche erscheint leider folgender Fehler: (if timenow - float(rowlog[0]) < 86400:
ValueError: could not convert string to float) Ich nehme an, dass dies daran liegt, dass der Header trotzdem eingelesen wird...
Das Skript funktioniert, wenn ich in der log.csv Datei die ersten zwei Zeilen (Header) lösche und natürlich csvreader2.next() weglasse.
BlackJack

@flambo: Statt zu raten ob die Header überlesen werden oder nicht, lass Dir in der Schleife doch einfach mal ausgeben was `rowlog` enthält. Das sollte nämlich eigentlich funktionieren.

Etwas komisch ist der Modus in dem Du die Datei öffnest. Das sollte wohl eher 'rb' sein.

Das Tupel mit den vielen ``rowlog[…]`` lässt sich mittels „slicing” und der `tuple()`-Funktion deutlich kürzer schreiben. Und die Liste liesse sich mit einer „list comprehension” kompakter schreiben.

Namen sollte man nicht durchnummerieren. Ich habe den Eindruck, dass Du viel zu viel Code auf Modulebene stehen hast und Dir Namen „ausdenken” musst.

Das ganze etwas kompakter und direkter ausgedrückt (`islice()` ist aus dem `itertools`-Modul) (ungetestet):

Code: Alles auswählen

with open('log.csv', 'rb') as logfile:
    now = time.time()
    compdata = [
        tuple(row[2:12])
        for row in islice(csv.reader(logfile, delimiter='\t'), 2, None)
        if now - float(row[0]) < 24 * 60 * 60
    ]
flambo
User
Beiträge: 10
Registriert: Mittwoch 7. März 2012, 15:05

hmm ich weiss was das Problem ist. Der Header wird nicht eingelesen mit next(). Also das funktioniert, aber in der Datei gibt es immer wieder Leerzeilen, welche nicht richtig eingelesen werden können, darum der im letzen Beitrag erwähnte Fehler.
Ich schreibe mit demselben Skript auch in die Datei, weiss aber nicht warum nach jeder Zeile mit Einträgen eine Leerzeile folgt....

Code: Alles auswählen

logfile3 = open("log.csv","a+")
csvwriter2=csv.writer(logfile3, delimiter='\t')

# Wenn unbekannte Geräte gefunden wurden und wenn diese nicht schon im Log-File sind dann wird ein Eintrag ins Log-File gemacht
if (len(nottrusted) > 0) and (len(unknownlog) > 0):
    t = time.strftime("%d.%m.%Y um %H:%M:%S Uhr")
    timestamp = str(time.time())
    for entry in unknownlog:
        daten = (timestamp,t,entry[0],entry[1],entry[2],entry[3],entry[4],entry[5],entry[6],entry[7],entry[8],entry[9],"not trusted")
        csvwriter2.writerow(daten)
logfile3.close() 
Wie hier zu sehen ist schreibe ich "daten" in das Logfile. Warum wird nun nach jeder Zeile mit den Einträgen von "daten" eine Leerzeile in das File geschrieben?

@BlackJack Das mit den tuplen werden ich im ganzen Script noch nachholen.. Ist hier auch nocht nicht gemacht..
BlackJack

@flambo: Du solltest vielleicht auch mal endlich mit dem 'a+'-Modus bei Dateien aufhören und Dich 1. auf das beschränken was Du brauchst und 2. den Binärmodus wählen. Das mit den zusätzlichen Leerzeilen könnte nämlich daran liegen falls Du Windows verwendest.

Falls nicht unbedingt Tupel benötigt werden, dann kannst Du auch einfach nur „slicen”. Im letzten Quelltext wäre eine Liste an der Stelle nämlich kein Problem.
flambo
User
Beiträge: 10
Registriert: Mittwoch 7. März 2012, 15:05

@BlackJack: Besten Dank es lag tatsächlich am Modus.. mit dem Binärmodus schreibt es ohne Leerzeilen. Vielen Dank für die Hilfe!!!
Antworten