CSV-Reader

Code-Stücke können hier veröffentlicht werden.
Antworten
hehejo
User
Beiträge: 56
Registriert: Freitag 18. Februar 2005, 18:24
Wohnort: Stein
Kontaktdaten:

Dienstag 29. März 2005, 09:36

Dank kompetenter Hilfe hier (http://python.sandtner.org/viewtopic.php?t=2992) kann ich dieses hier präsentieren:

Code: Alles auswählen

def csv(filename):
    f = open(filename, "r")
    bezeichner = f.readline().rstrip("\n").split(";")
    content = f.readlines()
    f.close()
    mappe = {}
    for line in content:
      if line.startswith("#"):
        continue
      line = line.rstrip("\n")
      for key, value in zip(bezeichner, line.split(";")):
        mappe[key] = value
      yield mappe
    #raise StopIteration
Zwar gibt es das Modul csv, aber .. naja ich wollt auch mal wieder was programmiern.

Kritik bitte äußern!

edit: Danke für die Hinweise Leonidas
Zuletzt geändert von hehejo am Dienstag 29. März 2005, 12:04, insgesamt 1-mal geändert.
Gruß, Johannes
[b][color=red]ascii stupid question,
get stupid ansii[/color][/b]
[url]http://www.hehejo.de[/url]
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 29. März 2005, 11:38

Es fehlt ein Doppelpunkt nach dem 1. for (ich weiß, Kleinigkeit) und sowie ich das sehe muss StopIteration nicht explizit aufgerufen werden, da Generatoren das immer von selbst tun, wenn sie am Ende sind (ist aber auch nicht so wild).
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Dienstag 29. März 2005, 21:40

Ich würd's so schreiben:

Code: Alles auswählen

def csv(lines, separator=';', comment_char='#'):
    line_iterator = iter(lines)
    header = line_iterator.next().rstrip().split(separator)
    for line in line_iterator:
        if not line.startswith(comment_char):
            yield dict(zip(header, line.rstrip().split(separator)))

csv_file = open('filename', 'r')
content = list(csv(csv_file))
csv_file.close()
Das herausziehen des Dateiöffnens hat den Vorteil, dass es nun mit jedem "iterable" funktioniert, das Zeilen liefert und nicht nur mit Dateien.

Ausserdem gebe ich nicht immer das selbe Dictionary zurück. Das kann zu sehr komischen Ergebnissen führen wenn man nicht damit rechnet. Zum Beispiel wäre bei 10 Datensätzen in `content` 10 mal das gleiche Dictionary mit dem letzten Datensatz als Inhalt.
hehejo
User
Beiträge: 56
Registriert: Freitag 18. Februar 2005, 18:24
Wohnort: Stein
Kontaktdaten:

Mittwoch 30. März 2005, 08:25

oh - das ist eine elegante Lösung.

Danke! Da werde ich mir wieder etwas rauszeiehen können.

Code: Alles auswählen

yield dict(zip(header, line.rstrip().split(separator)))
An sowas hab ich noch garnicht gedacht.
Wunderbar!
Gruß, Johannes
[b][color=red]ascii stupid question,
get stupid ansii[/color][/b]
[url]http://www.hehejo.de[/url]
BlackJack

Mittwoch 30. März 2005, 23:03

Vielleicht sollte man das Filtern von Kommentarzeilen und evt. auch Leerzeilen auch aus der Funktion herausnehmen. In der Form werden jetzt nur Kommentare nach der Zeile mit den Bezeichnern ignoriert, aber gerade ganz am Anfang einer Datei würde man ja Kommentare oder eine Beschreibung der folgenden Daten erwarten.

Code: Alles auswählen

from itertools import ifilter
import pprint

def csv(lines, separator=';'):
    line_iterator = iter(lines)
    header = line_iterator.next().rstrip().split(separator)
    for line in line_iterator:
        yield dict(zip(header, line.rstrip().split(separator)))

def make_linetest(comment_start='#'):
    def is_content_line(line):
        line = line.strip()
        return line and not line.startswith(comment_start)
    return is_content_line

csv_data = ('# Some names.\n',
            'name;surname;profession\n',
            ' # Another comment.\n',
            'Dagobert;Duck;Zillionaire\n',
            '  \t \n',
            'Guido;van Rossum;BDFL\n',
            '\n',
            '# Data ends here.\n')
content = list(csv(ifilter(make_linetest(), csv_data)))
pprint.pprint(content)
Ich geb zu, mit dem Closure habe ich es ein wenig übertrieben. Statt `make_linetest()` hätte es auch eine einfache Funktion mit hart kodiertem Kommentarzeichen getan. :-)
hehejo
User
Beiträge: 56
Registriert: Freitag 18. Februar 2005, 18:24
Wohnort: Stein
Kontaktdaten:

Freitag 1. April 2005, 09:24

BlackJack hat geschrieben:Vielleicht sollte man das Filtern von Kommentarzeilen und evt. auch Leerzeilen auch aus der Funktion herausnehmen. In der Form werden jetzt nur Kommentare nach der Zeile mit den Bezeichnern ignoriert, aber gerade ganz am Anfang einer Datei würde man ja Kommentare oder eine Beschreibung der folgenden Daten erwarten.
Das leuchtet mir ein.

Doch was ist das hier alles?

Code: Alles auswählen

from itertools import ifilter
import pprint
Was zur Hölle macht diese Funktion? Das verstehe ich jetzt im Moment nicht. Aber so wie ich das sehe, ist es eine Funktion, die man dann zum Sortieren/ Auswählen nutzen kann.

Code: Alles auswählen

def make_linetest(comment_start='#'):
    def is_content_line(line):
        line = line.strip()
        return line and not line.startswith(comment_start)
    return is_content_line
Könnsest du mir bitte dieses "return xxx and not xxx.xxx" erklären?
BlackJack hat geschrieben: Ich geb zu, mit dem Closure [...]
Closure? *öhmm* hast da mal nen Link zu?

Ich sehe schon: Python hat noch viele Feinheiten die ich entdecken kann!
Gruß, Johannes
[b][color=red]ascii stupid question,
get stupid ansii[/color][/b]
[url]http://www.hehejo.de[/url]
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 1. April 2005, 09:57

hehejo hat geschrieben:Doch was ist das hier alles?

Code: Alles auswählen

from itertools import ifilter
import pprint
itertools - zum effizienten erstellen von Iteratoren. ifilter() ist die Iteratoren-Version von filter().
pprint Prettyprint. Damit man was hübsches zu sehen hat :)
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Freitag 1. April 2005, 23:04

hehejo hat geschrieben:

Code: Alles auswählen

def make_linetest(comment_start='#'):
    def is_content_line(line):
        line = line.strip()
        return line and not line.startswith(comment_start)
    return is_content_line
Könnsest du mir bitte dieses "return xxx and not xxx.xxx" erklären?
Vielleicht wird's etwas klarer wenn ich Klammern setze:

Code: Alles auswählen

return (line and (not line.startswith(comment_start)))
Der Ausdruck nach ``return`` wird ausgewertet und zurückgegeben. Fangen wir mal hinten an: ``line.startswith(comment_start)`` gibt `True` oder `False` zurück. Mit ``not`` wird dieser Wert negiert, also als `False` wird `True` und umgekehrt. Und dieser Wahrheitswert wird mit `line` und-Verknüpft. Dazu muss man wissen, das eine Zeichenkette mit 0 Zeichen `False` ist und alles andere `True`. Das gleiche gilt übrigens für Listen, Tupel, Sets und alles andere auf das man `len()` anwenden kann. Noch etwas ausführlicher hätte man also auch das hier schreiben können:

Code: Alles auswählen

return ((len(line) != 0) and (not line.startswith(comment_start)))
BlackJack hat geschrieben: Ich geb zu, mit dem Closure [...]
Closure? *öhmm* hast da mal nen Link zu?
Klar: http://de.wikipedia.org/wiki/Closure

Python wird auf der Seite sogar erwähnt, die Beispiele sind leider in Perl :shock:

Und hier noch die englische Seite zum Thema http://en.wikipedia.org/wiki/Closure_(computer_science)
hehejo
User
Beiträge: 56
Registriert: Freitag 18. Februar 2005, 18:24
Wohnort: Stein
Kontaktdaten:

Montag 4. April 2005, 09:00

Vielen Dank!

Ich hab mir das ganze gestern noch mal durch den Kopf gehen lassen und bin wieder etwas klüger.

Gibt schon viele interessante Lösungen in Python...

Ich freu mich!
Gruß, Johannes
[b][color=red]ascii stupid question,
get stupid ansii[/color][/b]
[url]http://www.hehejo.de[/url]
Antworten