Sparkassen csv bereinigen

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Hallo,
aus der Sparkassen Online-Kontoführung kann man die Kontobewegungen als *csv herunterladen.
Leider befinden sich im Verwendungszweck Zeilenumbrüche, die die csv-Struktur durcheinander bringen:

Code: Alles auswählen

"Buchungstag";"Verwendungszweck";"Begünstigter/Zahlungspflichtiger";"Kontonummer";"BLZ";"Betrag";"Währung";"Info"
"13.04";"08.04/21.39UHR FooOrt";"GA NR000041238 BLZ43059991 3";"";"43059991";"-30,00";"Dollar";"Umsatz gebucht"
"02.04";"31.03/16.38UHR SPK.Foo G90
EUR00000020,00";"GA NR00001147 BLZ44050199 3";"9000411008";"44050199";"-20,00";"Dollar";"Umsatz gebucht"
"01.04";"UNTERHALT";"Foo, Bar";"123456789";"43059991";"18,00";"Dollar";"Umsatz gebucht"
<edit>Beispiel für Rohdaten überarbeited</edit>
Eben diese überflüssigen Zeilenumbrüche sollen entfernt werden:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso8859-15 -*-

Eingabe_Datei= file('/home/sabba/in/test-umsatz.csv', 'r')
Ausgabe_Datei= file('/home/sabba/in/ausgabe.csv', 'a')
ZeilenListe= Eingabe_Datei.readlines()

zaehler = len(ZeilenListe)
print "Anzahl der Zeilen in der Liste: ", zaehler
i=0
while i < zaehler :
    ZeilenListe[i] = ZeilenListe[i].replace( '\n' , '' ).replace( '";"' , '\t').strip('"')

    if ZeilenListe[i].endswith('Umsatz gebucht'):
        print "OK Zeile:\t",i+1, " --> ", ZeilenListe[i]

    elif ZeilenListe[i].endswith('hrung\tInfo'):
        del ZeilenListe[i]
        zaehler = len(ZeilenListe) 
        i=i-1 

    else:
        print "False Zeile:\t",i+1, " --> ", ZeilenListe[i]
        ZeilenListe[i] = TempString3=ZeilenListe[i] +" " + ZeilenListe[i+1]
        print "Korr. Zeile:\t", i+1, " --> ", ZeilenListe[i]
        del ZeilenListe[i+1]
        zaehler = len(ZeilenListe)
        i=i-1 

    i = i+1    #while ende

for i in range(len(ZeilenListe)):
    Ausgabe_Datei.write(ZeilenListe[i] + "\n")

Eingabe_Datei.close()
Ausgabe_Datei.close()
Mir ist klar, dieser Code läuft so nur auf Linux . Die Plattformunabhängigkeit kommt erst später, da ich mich damit noch nicht beschäftigt habe.
Das Pythonmodul für csv hat mich wegen mangelnder Unterstützung von Umlauten nicht weiter gebracht.

Meine Fragen gelten der while-Schleife:
1. Durch das Zurrücksetzen des Zählindexes "i" im elif- und else-Zweig habe ich da mir nicht so etwas wie ein "goto" eingebaut oder ist das eine normale Herangehensweise? Wäre hier Rekursion angebrachter?

2. In der 1. Zeile der while-Schleife habe ich zwei replace() und ein strip() hintereinander geschaltet, wie hättet ihr das gelöst - ich finde irgendwie sieht das nicht gesund aus? Oder ist das nur so ein Gefühl von mir?

Sebastian
Zuletzt geändert von Mr_Snede am Dienstag 12. Oktober 2004, 15:07, insgesamt 1-mal geändert.
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Ich bin eigentlich recht konservativ und nutze immer gerne soweit es geht builtin Module. Kannst du mir ein Beispiel zeigen, ebi dem csv versagt? Weil mir ist ein solches noch nicht untergekommen... Ansonsten müsste ich mich mal in deinen Code vertiefen, aber mir ist nicht so ganz klar was dein Code macht, da ich auch kein Beispiel zum Testen hab. Wenn es dir darum geht, csv Zeilen, in denen ein Zeilenumbruch steht, der aber insgesamt in Anführungszeichen steht, würde ich einen anderen Ansatz wählen. Ich würde mir ein Konstrukt aus regulären Ausdrücken basteln, dass das ganze für mich erledigt.

Zu deinen Fragen:
1.) in gewisser Weise ja, aber wohl für den gesamten Ansatz von dir notwändig. Ließe sich vielleich auf for-Schleifden umfornen, wobei dann auch mit Listenmethoden gearbeitet werden würde. Dann wäre die While Schleife raus und das ganze erschiene "sauberer"
2.) fällt mir spontan kein anderer Weg ein...
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Hallo Milan,

Mir geht es in erste Linie darum, dass in einigen Feldern, die Text enthalten eben auch Zeilenumbrüche enthalten sein können.
Eben diese Zeilenumbrüche sollen entfernt werden.

Das eingebaute Modul war auch meine erste Wahl.
Da es schon etwas her ist, dass ich damit Tests durchgeführt habe muss ich mir die Sachen noch einmal anschauen.
Soviel ich mich erinnere, wurden Umlaute und Sonderzeichen in den Textfeldern ersetzt ähnlich(gleich?) wie bei html: ä -> <&auml;>.

Dann hätte ich diese später ja wieder ersetzen müssen.

Ich werde den ersten Code-Block in meinem Posting gleich überarbeiten.
Du hast recht, er diente mir bis dahin selber zum Testen, er zeigt aber nicht alle Probleme, die ich in meinem Beitrag angesprochen habe.

cu Sebastian
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Also ich habe eben einen Test durchgeführt und es lief mit csv alles wunderbar, wurde auch alles korrekt geparst. Kann ja mal nachher einen Codeabschnitt dazu posten, aber ich denke das entschärft das ganze Problem :) .
Gast

Ich bins, der Mr_Snede.(bin gerade auf'm Sprung)
Werde mich später am Abend nochmal mit dem csv-Modul auseinandersetzen - und mich dann noch einmal melden.
danke Milan
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Mit dem Beispiel hier kannst du CSV-Text lesen und wieder in die Datei schreiben, ohne das es Probleme geben sollte. Kannst du ja mal testen und danach deine Veränderungen mit dem Code machen.

Code: Alles auswählen

import csv
class mydialect(csv.Dialect):
    delimiter=";"
    doublequote=True
    escapechar=None
    lineterminator="\n"
    quotechar='"'
    quoting=csv.QUOTE_ALL
    skipinitialspace=False

in_file=file('/home/sabba/in/test-umsatz.csv', 'r')
out_file= file('/home/sabba/in/ausgabe.csv', 'a')
#Ohne Veränderungen am Inhalt:
reader=csv.reader(in_file,mydialect)
writer=csv.writer(out_file,mydialect)
writer.writerows(reader)
in_file.close()
out_file.close()
Bei mir hat er jedenfalls ohne Probleme seinen Dienst getan.
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Code: Alles auswählen

import csv

class mydialect(csv.Dialect):
    delimiter=";"           
    lineterminator="\n"  
    quotechar='"'           
    doublequote=True    
    escapechar=None    
    quoting=csv.QUOTE_ALL 
    skipinitialspace=False     

in_file=file('/home/sabba/in/test-umsatz2.csv', 'r')
out_file= file('/home/sabba/in/ausgabe.csv', 'w')
reader=csv.reader(in_file,mydialect)

for row in reader:
    print row
    for field in row:
        if field.find('\n') >=0:
            print "--> mit LF: ", field
            field=field.replace( '\n' , ' ' )
            print "+-> ohne LF: ", field

print reader    
writer=csv.writer(out_file,mydialect)
writer.writerows(reader)
in_file.close()
out_file.close()
Hat ein wenig länger gedauert, - musste mich erst mit der csv-Doku vertraut machen.
Leider konnte ich die oben angegebenen Probleme mit dem csv-Modul nicht reproduzieren.

Mein Ziel ist es noch immer in keinem Feld einen Zeilenumbruch zu haben.

Leider weiss ich (trotz der Doku) nicht genau, was "reader" ist, bzw wie ich ihn richtig benutze.
Mit der kleinen for-Schleife schaffe ich es aus allen Feldern die Zeilenumbrüche zu entfernen.
Allerdings nur als Konsolenoutput. Im out_file kommt nichts an.

Meine Fragen:
Wie kann ich den Inhalt des reader-Objektes verändern (kann ich es überhaupt)?
Oder schreibe ich die durch meine for-Schleife bearbeiteten Zeilen(Listen) in eine Temporären Liste.
Um diese dann dem writer-Objekt zu übergeben?
Was habe ich mit dem reader in der Schleife angestellt?
--> "print reader" nach der Schleife gibt zummindest eine Adresse aus: <_csv.reader object at 0x40218dbc>
kann ich irgendwie anzeigen, was sich hinter dieser Adresse befindet?

Ich hoffe ich habe sinnvolle Fragen gestellt, und nicht andere nur verwirrt:-))
Ist garnicht so einfach herauszufinden, was man gerade nicht versteht.

Ein ziemlich müder Sebastian geht jetzt erst einmal ins Bett.
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Mr_Snede hat geschrieben:Leider weiss ich (trotz der Doku) nicht genau, was "reader" ist, bzw wie ich ihn richtig benutze.
Mit der kleinen for-Schleife schaffe ich es aus allen Feldern die Zeilenumbrüche zu entfernen.
Allerdings nur als Konsolenoutput. Im out_file kommt nichts an.
Hi. Sorry, ich wusste nicht, dass du noch nicht mit Iteratoren (Docupassage) gearbeitet hast. Diese sind beliebige Objekte, die irgendeine Ansammlung von Daten vertreten. Nehmen wir als Beispiel eine Liste. Iteratoren haben genau eine Methode: "next" . Diese Methode gibt immer ein Objekt wieder, bei einer Liste gibt sie beim ersten Aufruf das erste Element zurück. Beim nächsten das 2. usw... Wenn die Liste in unserem Beispiel durch ist, dann wird eine Excepttion ("StopIteration") losgetreten. Davon merkt der Anwender aber nix, weil die for-Schleife die Exception abfängt und nun weiß, dass sie ertig ist und es nix mehr zu verarbeiten gibt. Nun ist auch der Iterator verbraucht, alle Elemente sind ausgegeben (das ist auch der Grund, warum bei dir nix mehr ankommt). Der Zweck solcher Iteratoren ist es, beliebige Datentypen in for-Schleifen abhandeln zu können. Sie müssen nur aus sich einen Iterator erzeugen können (Methode "__iter__") und dann mit einer "next" Methode Input für die Schleife liefern. Das tuhen in der Praxis auch alle Datentypen von Python: Listen, Tupel, Strings, Sets und sogar Dateien (die dann Zeilenweise gelesen werden).

Was hilft dir das in deinem Problem? Viel. Zum einen kannst du einen Iterator natürlich auch komplett in einer Liste speichern, das kann aber mehr Speicher kosten, da es dem Iterator egal ist woher die Elemente kommen. Mitunter werden sie erst dann erzeugt, wenn sie gebraucht werden. In einer Liste werden alle immer gleich gebraucht :wink: . Das geht mit list(reader) (als würde in list eine for-Schleife stehen, die alles in eine Liste schreibt). Andererseits liefert der Iterator ja Listen zurück (for row in reader). Du kannst ja auch einfach die rows verändern und diese an den writer übergeben:

Code: Alles auswählen

class mydialect(csv.Dialect):
    delimiter=";"
    lineterminator="\n"
    quotechar='"'
    doublequote=True
    escapechar=None
    quoting=csv.QUOTE_ALL
    skipinitialspace=False

in_file=file('/home/sabba/in/test-umsatz2.csv', 'r')
out_file= file('/home/sabba/in/ausgabe.csv', 'w')
reader=csv.reader(in_file,mydialect)
writer=csv.writer(out_file,mydialect)

for row in reader:
    print row
    for i,field in enumerate(row):
        if field.find('\n') >=0:
            print "--> mit LF: ", field
            row[i]=field=field.replace( '\n' , ' ' )
            print "+-> ohne LF: ", field
    writer.writerow(row)

in_file.close()
out_file.close()
Der Trick liegt nun daran, dass ich die Liste selbst verändere (row=...) Das ganze geht über enumerate, auch ein Iterator. Man könnte enumerate so schreiben, wenn man davon ausgeht, dass herkömmliche Sequenzen da durchlaufen. In Wirklichkeit siehts aber anders aus, hab ja oben gesagt das Iteratoren nix anderes als next können müssen und somit len standartmäßig nicht unterstüzen:

Code: Alles auswählen

enumerate=lambda seq:zip(xrange(len(seq)),seq)
In Wirklichkeit siehts so aus, aber das auch noch in C geschrieben:

Code: Alles auswählen

def enumerate(seq):
    i=0
    for element in seq:
        yield (i,element)
        i+=1
Das nur mal als kleine Einstiegshilfe, ich hoffe du hast was davon verstanden :wink: . Hier noch ein letztes Beispiel, was du auch so aus Python kennen dürftest : xrange in Python.

mfg Milan
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Du brauchst dich nicht entschuldigen, habe ja nirgends vorher erwähnt dass ich blutiger Anfänger bin.
Nachdem ich mit Tutos nicht wirklich programmieren gelernt habe, schreibe ich halt ein Programm, dass ich selber gerne benutzen möchte.
Dass ich dabei neue Sachen kennenlerne ist geplant:-))

"for i,field in ... " das gefällt mir.
enumerate(row) habe ich auch verstanden, durch den 2.erklärenden Code und meiner Doku.
Mit den Lambda Anweisungen stehe ich noch auf Kriegsfuß - die knopf ich mir später mal vor:-)

Warum speicherst du

Code: Alles auswählen

field.replace( '\n' , ' ' )
nicht direkt in

Code: Alles auswählen

row[i]
? Sondern setzt da nocheinmal "field" dazwischen:

Code: Alles auswählen

row[i]=field=field.replace( '\n' , ' ' )
Der Gedanke alles in eine Liste zu tun:

Code: Alles auswählen

list(reader)
entspricht meiner bisherigen Idee.
Ich denke aber ich werde das Iteratorobjekt benutzen.

Milan - ich danke dir von ganzen Herzen für deine Hilfe.
Dank dir kann ich mich schon den nächsten Schritt angehen, die Lesezeichen sind schon in meinen Büchern.

cu Sebastian
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Mr_Snede hat geschrieben:Warum speicherst du

Code: Alles auswählen

field.replace( '\n' , ' ' )
nicht direkt in

Code: Alles auswählen

row[i]
? Sondern setzt da nocheinmal "field" dazwischen:

Code: Alles auswählen

row[i]=field=field.replace( '\n' , ' ' )
Hi. Das nennt sich eine Mehrfachzuweisung. Der Ausdruck mit dem replace wird nur einmal ausgewertet und gleich an zwei Objekte zugewiesen (Identität). Beide verweisen nun auf dasselbe Objekt. Solange das Objekt Immutable ist geht das gut, ansonsten kann da manchmal eine nicht gewollte "Verkettung" stattfinden, aber Strings sind ja immutable. Damit wollte ich erreichen, dass dein field noch für die Ausgabeanweisungen zur Verfügung steht und row weiterhin der Wert bleibt, der gespeichert wird. Ansonsten wäre es auch anders gegangen, aber ich war hier zu bequem :wink: .

mfg Milan
Antworten