Was werden wir behandeln? |
---|
Die Behandlung von Dateien bringt für Anfänger oft Probleme, obwohl der Grund dafür mich oft verblüfft. Dateien beim Programmieren sind nicht anders als Dateien, die du in einem Wortprozessor oder einer anderen Applikation verwendest: du öffnest sie, bearbeitest sie irgendwie und dann schließt du sie wieder.
Der größte Unterschied ist, dass du in einem Programm sequentiell auf die Files zugreifst, das heißt, du liest eine Zeile auf einmal am Anfang beginnend. In der Praxis tut der Wortprozessor das Gleiche, er behält das gesamte File im Speicher, während du daran arbeitest und schreibt dann wieder alles zurück, wenn du es schließt. Der andere Unterschied ist, dass du normalerweise ein File entweder nur zum Lesen oder nur zum Beschreiben öffnest. Du kannst eine Datei während ihrer Erzeugung von Grund auf (oder eine bereits existierende überschreiben) oder duch Anhängen an eine existierende schreiben .
Eine andere Sache, die du während der Filebearbeitung tun kannst, ist, du kannst zurück an den Anfang gehen.
Betrachten wir das in der Praxis. Wir nehmen an, es existiere eine Datei, menue.txt genannt, und die eine Liste von Gerichten enthalte:
frikadelle & eier frikadelle & fritten frikadelle & frikadelle
Jetzt werden wir ein Programm schreiben, um das File zu lesen und es auf den Bildschirm ausgeben - wie der 'cat'-Befehl in Unix oder das 'type' Kommando in DOS.
# Zuerst öffnet das File zum Lesen(r) inp = open("menue.txt","r") # liest die Datei in eine Liste und druckt dann # jedes Bestandteil for line in inp.readlines(): print line # schließt es wieder inp.close()
Anmerkung 1: open() nimmt zwei Argumente. Das erste ist der Filename (der als Variable oder Zeichenkette, so wie hier, übergeben werden kann). Das Zweite ist der Mode (Modus). Der Mode legt fest, ob wir die Datei zum Lesen (reading=r) oder Schreiben (writing = w) öffnen, und auch ob sie für ASCII-Text oder binären Gebrauch ist - durch Zufügen eines 'b' zum 'r' oder 'w', wie in: open(fn,"rb")
Anmerkung2: Wir lesen und schließen die Datei unter Verwendung von Funktionen, die durch die Filevariable vorgegeben werden. Diese Notation ist als Methodenaufruf bekannt und ist unser erstes Augenzwinkern nach Objekt-Orientierung. Doch mach dir jetzt darüber keine Gedanken, außer, dass du dir merkst, dass es in irgendeiner Weise in Bezug zu Modulen steht. Denke dir einfach, eine Filevariable als Referenz zu einem Modul, das Funktionen enthält, die mit Dateien arbeiten und die wir jedes mal automatisch importieren, sobald wir eine Filetypenvariable erzeugen.
Zu beachten ist, wie du mit langen Dateien umgehst. Zu allererst musst du die Datei auf einmal als einzige Zeile einlesen (in Python unter Verwendung von readline() anstatt von readlines()). Du musst dann eine Zeilenzählvariable verwenden, die für jede Zeile inkrementiert wird, und die dann überprüft wird, ob sie gleich 25 ist (für einen 25-Zeilen-Bildchirm). Wenn dem so ist, fordere den Anwender zu einem Tastendruck (sprich Enter) auf, bevor der Zeilenzähler auf Null zurückgesetzt wird und fortfährt. Du solltest dies einmal zur Übung versuchen ...
Das ist wirklich alles dazu. Du öffnest das File, ließt es ein und manipulierst es in der Art, wie du möchtest. Nach der Beendigung schließt du die Datei. Um in Python einen 'Copy'-Befehl auszuführen, öffnen wir einfach eine neue Datei im Schreibmode und schreiben die Zeilen in dieses File, anstatt diese auszudrucken. Etwa so:
# erzeugt das Äquivalent zu: COPY MENU.TXT MENU.BAK # öffne zuerst die Dateien zum Lesen(r) und Schreiben(w) inp = open("menu.txt","r") outp = open("menu.bak","w") # lies die Dateien in eine Liste und kopiere sie # in eine neue Datei for line in inp.readlines(): outp.write(line) print "1 File kopiert..." # schließe jetzt die Dateien inp.close() outp.close()
Ist dir aufgefallen, dass ich das Ausdrucken eines Satzes hinzugefügt habe, um dem Anwender rückzuversichern, dass etwas aktuelles geschieht? Diese Art von Anwenderrückmeldung (user feedback) ist eigentlich eine gute Idee.
Eine letzte knifflige Sache ist, wenn du Daten an das Ende einer existierenden Datei anhängen möchtest. Eine Art, dies zu tun, ist das File für die Eingabe zu öffnen, Lesen der Daten in eine Liste hinein, die Daten an die Liste anhängen und dann die ganze Liste rausschreiben auf eine neue Version der alten Datei. Wenn die Datei kurz ist, ist das kein Problem, aber wenn die Datei sehr groß ist, möglicherweise über 100Mb, dann wird einfach der Speicher überlaufen, um die Liste zu behalten. Glücklicherweise gibt es einen anderen Modus "a", den wir open() übergeben können, der uns erlaubt direkt an eine vorhandene Datei einfach durch Schreiben etwas anzuhängen. Und besser noch, wenn die Datei nicht existiert, wird es eine neue Datei öffnen, so als wäre sie mit "w" spezifiziert.
Als Beispiel wollen wir annehmen, wir hätten eine Log-Datei, die wir zur Anzeige von Fehlermeldungen verwenden würden. Wir möchten die existierenden Meldungen nicht löschen, so dass wir uns entscheiden, die Fehlermeldung so anzuhängen:
def logError(msg): err = open("Errors.log","a") err.write(msg) err.close()
In der Wirklichkeit würden wir wahrscheinlich die Dateigröße auf irgendeine Art begrenzen wollen. Eine übliche Technik ist es, einen Filenamen basierend auf dem Datum zu erzeugen, so dass wenn das Datum sich ändert, wir automatisch eine neue Datei erzeugen und es so leicht für die Betreuer des Systems ist, die Fehler eines bestimmten Tages zu untersuchen und nicht mehr benötigte alte Fehlerdateien zu archivieren.
Jetzt lass uns das Wortzählprogramm, das ich in dem vorhergehenden Artikel erwähnt habe, wieder betrachten. Errinere dich an den Pseudo Code, der so aussah:
def numworte(s): list = split(s) # Liste mit jedem Wort als Element return len(list) # Rückgabe der Elementzahl in der Liste for line in file: total = total + numworte(line) # akkumuliert Gesamtzahl für jede Zeile print "Die Datei hat %d Wörter" % total
Da wir jetzt wissen, wie wir die Zeilen von der Datei erhalten, lass uns den Rumpf der numworte() -Funktion betrachten. Zuerst wollen wir eine Liste von Worten in einer Zeile erzeugen. Beim Werfen eines Blickes in die Python-Referenzdokumentation nach dem string-Modul, sehen wir, dass es dort eine Funktion namens split gibt, die einen String in eine Liste von Feldern hinein durch Leerzeichen (oder jedes andere von uns definierte Zeichen)getrennt separiert. Endlich, bei nochmaligem zu-Rate-ziehen der Dokumentation, sehen wir, dass die eingebaute Funktion len() die Zahl der Elemente in einer Liste zurückgibt, was in unserem Fall die Zahl der Worte in dem String sein soll - exakt was wir wollen.
Am Ende sieht der Code so aus:
import string def numworte(s): list = string.split(s) # erfordert die Qualifizierung split() mit dem Stringmodul return len(list) # gibt die Anzahl der Elemente in einer Liste zurück inp = open("menue.txt","r") total = 0 # initialisiert auf Null; erzeugt auch eine Variable for line in inp.readlines(): total = total + numworte(line) # akkumuliert Gesamtzahl für jede Zeile print "Die Datei hat %d Worte" % total inp.close()
Das ist eigentich nicht ganz richtig, weil der '&'-Character als ein Wort zählt (obwohl es sein mag, dass du denkst es sollte...). Also, dies kann nur auf ein einzelnes File (nenue.txt) angewendet werden. Aber es ist nicht zu schwer es so zu konvertieren, dass es den Filenamen von der Kommandozeile ( argv[1]) oder über raw_input() , wie wir es im Kapitel 'Gespräch mit dem Benutzer' sahen, liest. Ich lasse das als eine Übung für den Leser übrig.
BASIC und Tcl sind mit ihren eigenen Dateibehandlungsmechanismen ausgestattet. Diese sind nicht allzu verschieden von Python, so dass ich einfach von beiden das 'cat'-Programm zeige und es dann dabei belasse.
BASIC verwendet ein Konzept zur Identifizierung von Dateien, das Ströme genannt wird.Diese Ströme sind durchnummeriert, was die BASIC-Datenbehandlung recht langweilig machen kann. Dies kann durch die Verwendung einer ganz nützlichen Funktion, genannt ???, vermieden werden, die die nächste freie Stromzahl zurückgibt. Wenn du diese in einer Variablen speicherst, wirst du nicht mehr dadurch verwirrt, welche Strom/File welche Nummer hat.
INFILE = FREEFILE
OPEN "TEST.DAT" FOR INPUT AS INFILE
REM überprüfe auf EndOfFile(EOF) dann
REM lese Zeile von Eingabe und drucke sie
DO WHILE NOT EOF(INFILE)
LINE INPUT #INFILE, dieZeile
PRINT dieZeile
CLOSE #INFILE
Das Muster sollte jetzt klar sein. Hier ist die Tcl-Version:
set infile [open "Test.dat" r] while { [gets $infile Zeile] >= 0} { puts $Zeile } close $infile
Zur Erinnerung |
---|
Im Falle von Fragen oder Hinweisen zu dieser Seite sende eine
Nachricht auf Englisch an: alan.gauld@yahoo.co.uk
oder aud Deutsch an bup.schaefer@freenet.de