zurück rauf weiter Englisch Index

Kapitel 11

Rohübersetzung -- bitte um Rückmeldungen über Fehler und Unklarheiten an glingl@aon.at

Dateien und Ausnahmen

Solange ein Programm läuft, befinden sich seine Daten im Arbeitsspeicher. Wenn das Programm endet oder der Computer heruntergefahren wird, dann verschwinden die Daten aus dem Arbeitsspeicher. Um Daten dauerhaft zu sichern, muss man sie in einer Datei abspeichern. Dateien werden üblicherweise auf einer Festplatte, Diskette oder CD-ROM gespeichert.

Wenn man es mit einer großen Zahl von Dateien zu tun hat, sind diese oftmals in sogenannten Verzeichnissen (auch "Ordner" genannt) organisiert. Jede Datei wird durch einen eindeutigen Namen oder durch eine Kombination von Dateinamen und Verzeichnisnamen identifiziert.

Programme können Information miteinander austauschen, indem sie Dateien lesen und schreiben und druckbare Formate, wie z. B. PDF, erzeugen.

Die Arbeit mit Dateien ist in mancher Hinsicht wie die Arbeit mit Büchern. Um ein Buch zu benützen, muss man es öffnen. Wenn man fertig ist, muss man es wieder schließen. Solange es offen ist, kann man entweder etwas hineinschreiben oder etwas herauslesen. In beiden Fällen weiß man, wo in dem Buch man gerade ist. Meistens liest man die Seiten eines Buchs in ihrer natürlichen Reihenfolge, aber man kann ebenso auch darin herumspringen.

All das trifft auch für Dateien zu. Um eine Datei zu öffnen, gibt man ihren Namen an und ob man darin lesen oder schreiben will.

Indem man eine Datei öffnet, erzeugt man ein Datei-Objekt. Im folgenden Beispiel verweist die Variable f auf dieses neue Datei-Objekt.

>>> f = open("test.dat","w")
>>> print f
<open file 'test.dat', mode 'w' at fe820>

Der open Funktion werden zwei Argumente übergeben. Das erste ist der Name der Datei und das zweite der Modus, in dem sie geöffnet wird. Modus "w" (für write) bedeutet, dass die Datei zum Schreiben geöffnet wird.

Wenn keine Datei des Namens test.dat vorhanden ist, wird sie erzeugt werden. Wenn es sie bereits gibt, wird sie durch die Datei, die gerade erzeugt wird, ersetzt werden.

Wenn wir das Datei-Objekt ausgeben, sehen wir den Namen der Datei, den Modus und den Ort des Objekts.

Um Daten in die Datei zu schreiben, rufen wir die write Methode für das Dateiobjekt auf:

>>> f.write("Now is the time")
>>> f.write("to close the file")

Wenn man die Datei schließt, weiß das System, dass jetzt nichts mehr geschrieben wird und es bereitet die Datei fürs Lesen vor.

>>> f.close()

Nun kann man die Datei neuerlich öffnen, diesmal zum Lesen, und den Inhalt der Datei in einen String einlesen. Dazu muss man das Modus-Argument auf "r" (für read), also auf Lesen, setzen.

>>> f = open("test.dat","r")

Wenn man versucht eine Datei zum Lesen zu öffnen, die es nicht gibt, erhält man eine Fehlermeldung:

>>> f = open("test.cat","r")
IOError: [Errno 2] No such file or directory: 'test.cat'

Es ist wohl nicht besonders überraschend, dass die read Methode Daten aus der Datei liest. Ohne Argument liest sie den ganzen Inhalt der Datei:

>>> text = f.read()
>>> print text
Now is the timeto close the file

Es ist da kein Leerzeichen zwischen "time" und "to", weil wir kein Leerzeichen zwischen diese beiden Strings geschrieben haben.

read kann auch ein Argument übernehmen, das angibt, wieviele Zeichen gelesen werden sollen:

>>> f = open("test.dat","r")
>>> print f.read(5)
Now i

Wenn nicht mehr genug Zeichen in der Datei sind, dann gibt read die restlichen Zeichen zurück. Wenn wir ans Ende der Datei kommen, dann gibt read den Leerstring zurück:

>>> print f.read(1000006)
s the timeto close the file
>>> print f.read()

>>>

Die folgende Funktion kopiert eine Datei, indem sie bis zu fünfzig Zeichen aufeinmal liest, bzw. schreibt. Das erste Argument ist der Name der urspünglichen Datei; das zweite ist der der neuen Datei:

def copyFile(oldFile, newFile):
  f1 = open(oldFile, "r")
  f2 = open(newFile, "w")
  while 1:
    text = f1.read(50)
    if text == "":
      break
    f2.write(text)
  f1.close()
  f2.close()
  return

Die break Anweisung ist hier neu. Wenn sie ausgeführt wird, springt die Programmausführung aus der Schleife heraus und setzt bei der ersten Anweisung nach der Schleife fort.

In diesem Beispiel ist die while Schleife endlos, weil der Wert 1 immer wahr ist. Der einzige Weg aus der Schleife geht über die Ausführung der break Anweisung, und diese geschieht, wenn text der Leerstring ist. Das wiederum geschieht, wenn wir am Ende der Datei angelangt sind.

11.1 Textdateien

Eine Textdatei ist eine Datei, die druckbare Zeichen und nicht sichtbare Zeichen ("whitespace") enthält, die in einzelnen Zeilen organisiert sind , die durch "newline"-Zeichen voneinander getrennt sind. Da Python speziell in Hinblick auf die Verarbeitung von Textdateien entworfen worden ist, stellt es Methoden zur Verfügung, die diesbezüglicher Aufgaben ganz leicht machen.

Als Demonstration werden wir eine Textdatei mit drei Zeilen Text, getrennt durch "newline"-Zeichen, erzeugen:

>>> f = open("test.dat","w")
>>> f.write("line one\nline two\nline three\n")
>>> f.close()

Die readline Methode liest alle Zeichen bis zu und einschließlich dem nächsten "newline"-Zeichen:

>>> f = open("test.dat","r")
>>> print f.readline()
line one

>>>

readlines gibt alle verbleibenden Zeilen als eine Liste von Strings zurück:

>>> print f.readlines()
['line two\012', 'line three\012']

In diesem Fall ist die Ausgabe im Listen-Format, d. h. die Strings erscheinen mit Anführungszeichen und das "newline" Zeichen erscheint als Escape-Sequenz <br>012.

Am Ende der Datei gibt readline den Leerstring zurück, bzw. readlines die leere Liste.

>>> print f.readline()

>>> print f.readlines()
[]

Das Folgende ist ein Beispiel für ein Programm, das ein Textfile zeilenweise verarbeitet. filterFile erzeugt eine Kopie von oldFile, wobei alle Zeilen, die mit # beginnen, ausgelassen werden:

def filterFile(oldFile, newFile):
  f1 = open(oldFile, "r")
  f2 = open(newFile, "w")
  while 1:
    text = f1.readline()
    if text == "":
      break
    if
text[0] == '#':
      continue
    f2.write(text)
  f1.close()
  f2.close()
  return

Die continue Anweisung beendet den aktuellen Schleifendurchlauf, nicht aber die Schleife insgesamt. Die Programmausführung springt an den Anfang der Schleife, prüft die Bedingung und fährt entsprechend fort.

Wenn nun text gleich dem Leerstring ist, wird die Schleife beendet. Wenn das erste Zeichen von text unser Kanalgitter ist, geht die Programmausführung wieder an den Anfang der Schleife. Nur wenn beide Bedinungen nicht eintreten, wird text in die neue Datei kopiert.

11.2 Werte von Variablen schreiben

Das Argument von write muss ein String sein, d. h. wenn wir Werte anderer Typen in eine Datei schreiben wollen, müssen wir diese zuerst in einen String konvertieren. Das geht am einfachsten mit der str Funktion:

>>> x = 52
>>> f.write (str(x))

Eine Alternative stellt der Format-Operator % dar. Wenn man ihn auf Ganzzahlen anwendet, dann ist % der modulus-Operator. Aber wenn der linke Operand ein String ist, dann ist % der Format-Operator.

Der linke Operand ist der Format-String und der rechte Operand ist ein Tupel von Ausdrücken. Das Ergebnis ist ein String, der die Werte der Ausdrücke enthält und zwar formatiert entsprechend dem Format-String.

Ein einfaches Beispiel: die Format-Sequenz "%d" bedeutet, dass der erste Ausdruck in dem Tupel als eine Ganzzahl formatiert werden sollte. Hier steht der Buchstabe d für "dezimal":

>>> cars = 52
>>> "%d" % cars
'52'

Das Ergebnis ist der String '52', der nicht mit dem Gazzahlwert 52 verwechselt werden darf.

Eine Format-Sequenz kann überall in einem Format-String auftreten. Somit können wir Werte in Sätze einbetten:

>>> cars = 52
>>> "In July we sold %d cars." % cars
'In July we sold 52 cars.'

Die Format-Sequenz "%f" formatiert das nächste Element in dem Tupel als eine Gleitkommazahl und "%s" formatiert das nächste Element als einen String:

>>> "In %d days we made %f million %s." % (34,6.1,'dollars')
'In 34 days we made 6.100000 million dollars.'

In der Voreinstellung druckt das Gleitkomma-Format sechs Dezimalstellen.

Die Anzahl der Ausdrücke in dem Tupel muss mit der Anzahl der Format-Sequenzen in dem String übereinstimmen. Ebenso müssen die Typen der Ausdrücke mit den Typangaben in den Format-Sequenzen übereinstimmen:

>>> "%d %d %d" % (1,2)
TypeError: not enough arguments for format string
>>> "%d" % 'dollars'
TypeError: illegal argument type for built-in operation

Im ersten Beispiel sind nicht genug Ausdrücke vorhanden; im zweiten hat der Ausdruck den falschen Typ.

Um das Format von Zahlen besser kontrollieren zu können, kann man die Anzahl der Ziffern als Teil der Format-Sequenzen angeben:

>>> "%6d" % 62
'    62'
>>> "%12f" % 6.1
'    6.100000'

Die Zahl nach dem Prozentzeichen ist die Minimalzahl von Stellen, die die Zahl einnehmen wird. Wenn der eingesetzte Wert weniger Ziffern hat, werden links führende Leerzeichen eingefügt. Wenn die Anzahl der Stellen negativ ist, werden rechts abschließende Leerzeichen angefügt:

>>> "%-6d" % 62
'62    '

Für Gleitkommazahlen können wir auch noch die Anzahl der Stellen nach dem Dezimalpunkt angeben:

>>> "%12.2f" % 6.1
'        6.10'

In diesem Beispiel nimmt das Ergebnis 12 Stellen ein und dies schließt zwei Stellen hinter dem Komma ein. Dieses Format ist z.B. nützlich für die Ausgabe mehrerer Dollarbeträge mit stellenwertrichtig untereinander geschriebenen Dezimalpunkten.

Man stelle sich beispielsweise ein Dictionary vor, das die Namen von StudentInnen als Schlüssel und Stundenlöhne als Werte enthält. Hier ist eine Funktion, die den Inhalt des Dictionary als formatierte Tabelle ausgibt.

def report (wages) :
  students = wages.keys()
  students.sort()
  for student in students :
    print "%-20s %12.02f" % (student, wages[student])

Um diese Funktion zu testen, werden wir ein kleines Dictionary erzeugen und den Inhalt ausgeben:

>>> wages = {'mary': 6.23, 'joe': 5.45, 'joshua': 4.25}
>>> report (wages)
joe                          5.45
joshua                       4.25
mary                         6.23

Indem man den Platzbedarf jedes Wertes kontrolliert, kann man garantieren, dass die Spalten zusammenpassen, solange die Namen weniger als 21 Zeichen enthalten und die Löhne geringer sind als eine Milliarde Dollars pro Stunde.

11.3 Verzeichnisse

Wenn man eine neue Datei erzeugt, indem man sie öffnet und schreibt, dann wird die Datei in das aktuelle Arbeitsverzeichnis geschrieben (in der Regel das Verzeichnis, von dem aus man das Programm gestartet hat). Ebenso ist es wenn man eine Datei zum Lesen öffnet: Python sucht sie im aktuellen Arbeitsverzeichnis.

Möchte man eine Datei öffnen, die irgendwo anders steht, dann muss man den Pfad zu dieser Datei angeben. Das ist der Name des Verzeichnisses (Ordners), wo die Datei ist. Auf einem Unix/Linux - System könnte das etwa so aussehen:

>>>   f = open("/usr/share/dict/words","r")
>>>   print f.readline()
Aarhus

Diese Beispiel öffnet eine Datei namens words, die in einem Verzeichnis namens dict liegt, das sich in share befindet, welches sich seinerseits in usr befindet, welches wiederum im Wurzel-Verzeichnis des Systems genannt / zu finden ist.

Natürlich kann man / nicht als Teil eines Dateinamens verwenden; dieses Zeichen ist reserviert für die Verwendung als Begrenzer zwischen Verzeichnis- bzw. Dateinamen.

Die Datei /usr/share/dict/words enthält eine Liste von Wörtern in alphabetischer Reihenfolge, von denen das erste der Namen einer dänischen Universität ist.

Amerkung d. Ü.: Für ein Windows-System müßte der obige Code etwa folgendermaßen modifiziert werden, weil dort der Begrenzer von Verzeichnis- und Dateinamen nicht der slash / sondern der backslash \ ist, der aber bei jedem Auftreten mittels eines weiteren backslash seiner Escape-Zeichen-Eigenschaft beraubt werden muss. daher zweimal \ :

>>>   f = open("C:\\user\\share\\dict\\words","r")
>>>   print f.readline()
Aarhus

11.4 Picklen

Um Werte in eine Datei zu schreiben, muss man sie in Strings verwandeln. Wir haben schon gesehen, wie man das mit str machen kann:

>>> f.write (str(12.3))
>>> f.write (str([1,2,3]))

Das Problem ist aber dabei, dass man, wenn man die Werte wieder von der Datei einliest, jedenfalls einen String erhält. Die ursprüngliche Typinformation ist verloren gegangen. In der Tat kann man nicht einmal mehr sagen, wo ein Wert endet und der nächste beginnt:

>>>   f.readline()
'12.3[1, 2, 3]'

Die Lösung für dieses Problem ist das Picklen (sicherlich keine schlimmere Wortkreation als Downloaden     man denke etwa an mixed pickles, wo ja auch verschiedenartigste Objekte zur späteren Verwendung durcheinander eingelegt werden (Anm. d.Ü.)). Picklen wird so genannt, weil es Datenstrukturen "bewahrt". Der pickle Modul enthält die nötigen Kommandos. Um ihn zu benützen, importieren wir pickle und öffnen dann dei Datei in üblicher Weise:

>>> import pickle
>>> f = open("test.pck","w")

Um eine Datenstruktur zu speichern, benutzt man die dump Methode und schließt dann die Datei wie übliche:

>>> pickle.dump(12.3, f)
>>> pickle.dump([1,2,3], f)
>>> f.close()

Später kann man die Datei zum Lesen öffnen und die abgelegten Datenstrukturen wieder laden:

>>> f = open("test.pck","r")
>>> x = pickle.load(f)
>>> x
12.3
>>> type(x)
<type 'float'>
>>> y = pickle.load(f)
>>> y
[1, 2, 3]
>>> type(y)
<type 'list'>

Mit jedem Aufruf von load bekommen wir einen einzelnen Wert aus der Datei, vollständig, mit seinem ursprünglichen Typ.

11.5 Ausnahmen

Wann immer ein Laufzeitfehler auftritt, wird eine Ausnahme erzeugt. Normalerweise stoppt dann das Programm und Python gibt eine Fehlermeldung aus.

Beispielsweise erzeugt die Division durch null eine Ausnahme:

>>> print 55/0
ZeroDivisionError: integer division or modulo

Ebenso der Zugriff auf ein nicht existierendes Listenelement:

>>> a = []
>>> print a[5]
IndexError: list index out of range

Oder der Zugriff auf einen Schlüssel, der in einem Dictionary nicht vorkommt:

>>> b = {}
>>> print b['what']
KeyError: what

In jedem Fall besteht die Fehlermeldung aus zwei Teilen: Der Typ des Fehlers steht vor dem Doppelpunkt Detailangaben über den Fehler nach dem Doppelpunkt. Normalerweise gibt Python auch eine Beschreibung des Aufrufstacks an der Stelle wo das Programm war, als der Fehler auftrat, aus. Diesen Teil haben wir bei den folgenden Beispielen weggelassen.

Manchmal will man eine Operation ausführen, die eventuell eine Ausnahme, also einen Laufzeitfehler, versursachen kann, aber trotzdem verhindern, dass in diesem Fall das Programm abbricht. Man kann diese Ausnahme behandeln, indem man die try und except - Anweisungen verwendet.

Als Beispiel möchten wir den Benutzer um einen Dateinamen fragen und dann versuchen, die entsprechende Datei zu öffnen. Wenn es die Datei nicht gibt, dann soll das Programm nicht abbrechen, sondern wir möchten diese Ausnahme abfangen:

filename = raw_input('Enter a file name: ')
try:
  f = open (filename, "r")
except:
  print 'There is no file named', filename

Die try Anweisung führt die Anweisungen im ersten Block aus. Wenn keine Ausnahme auftritt, wird die except Anweisung ignoriert. Wenn doch eine Ausnahme auftritt, werden die Anweisungen im except Zweig und im Anschluß daran das Programm weiter ausgeführt.

Wir können diese Funktionalität in einer Funktion kapseln: Der Funktion exists wird ein Dateinamen als Argument übergeben und sie gibt wahr zurück, falls die Datei existiert und falsch andernfalls:

def exists(filename):
  try:
    f = open(filename)
    f.close()
    return 1
  except:
    return 0

Man kann sogar mehrfache except Blöcke verwenden, um verschiedene Arten von Ausnahmen zu behandeln. Im Python Reference Manual stehen die Details.

Wenn ein Programm eine Fehlerbedingung erkennt, kann man mit der raise Anweisung erzwingen, dass eine Ausnahme erzeugt wird. Hier ist ein Beispiel, das eine Benutzereingabe einliest und feststellt, dass der Wert gleich 17 ist. Wir nehmen an, aus irgendwelchen Gründen wäre 17 keine gültige Eingabe, daher erzeugen wir eine Ausnahme:

def inputNumber () :
  x = input ('Pick a number: ')
  if x == 17 :
    raise 'BadNumberError', '17 is a bad number'
  return x

Die raise Anweisung übernimmt zwei Argumente: den Ausnahmetyp und eine spezifische Information über den Fehler. BadNumberError ist eine neue Art von Ausnahme, die wir gerade für diese Anwendung erfunden haben.

Wenn die Funktion, die inputNumber aufgerufen hat, den Fehler behandelt, dann kann das Programm weiter ausgeführt werden; anderfalls gibt Python eine Fehlermeldung aus und bricht die Programmausführung ab:

>>> inputNumber ()
Pick a number: 17
BadNumberError: 17 is a bad number

Die Fehlermeldung enthält den Ausnahmetyp und die Zusatzinformation, die man bereitgestellt hat.

Übung: schreibe eine Funktion, die inputNumber benützt, um eine Zahl über die Tastatur einzulesen und die den BadNumberError in geeigneter Weise abfängt.

11.6 Glossar

Datei
Ein mit Namen versehenes Objekt, üblicherweise auf einer Festplatte, einer Diskette oder einer CD_ROM gespeichert, das einen Strom (eine Folge) von Zeichen enthält.
file
A named entity, usually stored on a hard drive, floppy disk, or CD-ROM, that contains a stream of characters.
Verzeichnis
Eine mit Namen versehene Kollektion von Dateien, auch Ordner genannt.
directory
A named collection of files, also called a folder.
Pfad
Eine Folge von Verzeichnisnamen, die den exakten Speicherort einer Datei angeben.
path
A sequence of directory names that specifies the exact location of a file.
Textdatei
Eine Datei, die druckbare Zeichen, in Zeilen angeordnet, enthält. Die Zeilen werden durch "newline"-Zeichen voneinander getrennt.
text file
A file that contains printable characters organized into lines separated by newline characters.
break Anweisung
Eine Anweisung, die bewirkt, dass die Programmausführung eine Schleife verläßt.
break statement
A statement that causes the flow of execution to exit a loop.
continue Anweisung
Eine Anweisung, die die aktuelle Ausführung eines Schleifendurchganges beendet. Die Programmausführung springt zum Schleifenkopf, wertet die Bedingung aus und fährt entsprechend fort.
continue statement
A statement that causes the current iteration of a loop to end. The flow of execution goes to the top of the loop, evaluates the condition, and proceeds accordingly.
Format-Operator
Der % Operator. Der linke Operand ist ein Format-String, der rechte ein Tupel von Ausdrücken. Das Ergebnis der Operation ist ein String, der die Ausdrücke so formatiert enthält, wie es der Format-String angibt.
format operator
The % operator takes a format string and a tuple of expressions and yields a string that includes the expressions, formatted according to the format string.
Format-String
Ein String, der druckbare Zeichen und Format-Sequenzen enthält, die anzeigen, wie bestimmte Werte formatiert werden sollen.
format string
A string that contains printable characters and format sequences that indicate how to format values.
Format-Sequenz
Eine Folge von Zeichen, die mit % beginnt und angibt, wie ein Wert formatiert werden soll .
format sequence
A sequence of characters beginning with % that indicates how to format a value.
pickle
Einen Datenwert in eine Datei schreiben, zusammen mit seinen Typ-Informationen, sodass er später rekonstruiert werden kann.
pickle
To write a data value in a file along with its type information so that it can be reconstituted later.
Ausnahme
Ein Fehler, der während der Laufzeit auftritt.
exception
An error that occurs at runtime.
Fehlerbehandlung
Verhindern, dass wegen einer Ausnahme die Programmausführung endet. Geschieht mit den try bzw. except Anweisungen.
handle
To prevent an exception from terminating a program using the try and except statements.
raise
Eine Ausnahme mit Hilfe der raise Anweisung erzeugen.
raise
To signal an exception using the raise statement.


zurück rauf weiter Englisch Index