Unterabschnitte


7. Ein- und Ausgabe

Es gibt verschiedene Wege, um die Ausgabe eines Programmes darzustellen. Daten können in einer von Menschen lesbaren Form ausgegeben werden oder zur späteren Verwendung in eine Datei geschrieben werden. Dieses Kapitel diskutiert einige der Möglichkeiten.


7.1 Ansprechendere Formatierung von Ausgaben

Bisher haben wir zwei Arten kennengelernt, Werte auszugeben: Ausdrücke und die print-Anweisung. (Eine weitere ist die Methode write() von Datei-Objekten zu verwenden. Die Standard-Ausgabe kann durch sys.stdout als Datei-Objekt referenziert werden. Siehe die Bibliotheks-Referenz für weitere Information zu diesem Thema.)

Oftmals möchte man die Formatierung seiner Ausgabe besser gestalten, als nur durch Leerzeichen zwischen den Werten. Es gibt zwei Arten, seine Ausgabe zu formatieren. Die erste ist, die gesamte Handhabung von Strings selbst zu übernehmen. Mit den Operationen zur Teilbereichs-Bildung und Aneinanderfügung von Strings kann man jedes vorstellbare Format erzeugen. Das Standard-Modul string enthält einige nützliche Operationen, um Strings bis zu einer vorgegebenen Spaltenbreite aufzufüllen. Diese Operationen werden in Kürze besprochen. Die zweite Art ist, den Operator % mit einem String als erstem Argument zu verwenden. % interpretiert das linke Argument als sprintf()-ähnliches Format wie in C, das auf das rechte Argument angewendet wird und gibt den String nach der Anwendung dieses Operators zurück.

Eine Frage jedoch bleibt: wie wandelt man Werte in Strings um? Zum Glück kennt Python eine Möglichkeit, jeden Wert in einen String umzuwandeln, nämlich, ihn der Funktion repr() zu übergeben oder einfach, den Wert zwischen umgekehrten einfachen Anführungszeichen zu schreiben (``). Einige Beispiele:


>>> x = 10 * 3.14
>>> y = 200*200
>>> s = 'The value of x is ' + `x` + ', and y is ' + `y` + '...'
>>> print s
The value of x is 31.4, and y is 40000...
>>> # Umgekehrte Anfuehrungszeichen funktionieren
>>> # nicht nur bei Zahlen:
... p = [x, y]
>>> ps = repr(p)
>>> ps
'[31.4, 40000]'
>>> # Die Konvertierung eines Strings fuegt Anfuehrungszeichen
>>> # und Rueckwaerts-Schraegstriche hinzu:
... hello = 'hello, world\n'
>>> hellos = `hello`
>>> print hellos
'hello, world\012'
>>> # Das Argument von umgekehrten Anfuehrungszeichen
>>> # darf ein Tuple sein:
... `x, y, ('spam', 'eggs')`
"(31.4, 40000, ('spam', 'eggs'))"

Dies sind zwei Arten, eine Tabelle mit Quadrat- und Kubikzahlen auszugeben:


>>> import string
>>> for x in range(1, 11):
...     print string.rjust(`x`, 2), string.rjust(`x*x`, 3),
...     # Beachte abschliessendes Komma in der Zeile zuvor!
...     print string.rjust(`x*x*x`, 4)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

(Man beachte, daß ein Leerzeichen zwischen den Spalten durch die Art hinzugefügt wurde, wie print funktioniert: es fügt immer ein Leerzeichen zwischen seinen Argumenten ein.)

Dieses Beispiel demonstriert, wie man mit der Funktion string.rjust() einen String in einer Tabelle mit vorgegebener Breite rechtsbündig ausgeben kann, indem links Leerzeichen hinzugefügt werden. Es gibt ähnliche Funktionen string.ljust() und string.center(). Diese Funktionen geben nichts aus, sondern geben einfach einen neuen String zurück. Falls der Eingabestring zu lang ist, schneiden sie ihn nicht ab, sondern geben ihn unverändert zurück. Das wird die Anordnung der Spalten etwas durcheinanderbringen, aber das ist normalerweise immer noch besser, als die Alternative, nämlich einen verfälschten Wert auszugeben. Wenn man wirklich das Abschneiden braucht, kann man immer eine Teilbereichs-Operation verwenden, wie z.B. in "string.ljust(x, n)[0:n]".)

Es gibt eine weitere Funktion, string.zfill(), die einen numerischen String links mit Nullen auffüllt. Sie kann auch mit Plus- und Minus-Vorzeichen umgehen:


>>> string.zfill('12', 5)
'00012'
>>> string.zfill('-3.14', 7)
'-003.14'
>>> string.zfill('3.14159265359', 5)
'3.14159265359'
Die Verwendung des %-Operators sieht so aus:


>>> import math
>>> print 'The value of PI is approximately %5.3f.' % math.pi
The value of PI is approximately 3.142.

Gibt es mehr als eine Format-Anweisung in dem String, übergibt man ein Tupel als rechten Operanden, z.B.


>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> for name, phone in table.items():
...     print '%-10s ==> %10d' % (name, phone)
...
Jack       ==>       4098
Dcab       ==>    8637678
Sjoerd     ==>       4127

Die meisten Format-Anweisungen funktionieren genau wie in C und verlangen, daß man den passenden Typ übergibt. Tut man dies jedoch nicht, so wird eine Ausnahme ausgelöst und es wird kein ,,core dump`` erzeugt. Das %s-Format ist nicht ganz so streng: wenn das entsprechende Argument kein String ist, wird es mit der eingebauten Funktion str() in einen solchen umgewandelt. Die Verwendung von * zur Übergabe der Breite oder Genauigkeit als separates (Ganzzahl-) Argument wird auch unterstützt. Die C-Format-Anweisungen %n und %p werden nicht unterstützt.

Hat man einen wirklich langen Format-String, den man nicht aufteilen möchte, wäre es nett, die Variablen mit Namen zu referenzieren, anstatt ihre Position anzugeben. Das kann geschehen, indem eine Erweiterung von C-Format-Anweisungen in der Form %(name)format verwendet wird, z.B.


>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Das ist besonders nützlich in Kombination mit der eingebauten Funktion vars(), die ein Dictionary mit allen lokalen Variablen zurück gibt.


7.2 Dateien lesen und schreiben

open() gibt ein Datei-Objekt zurück und wird meist mit zwei Argumenten verwendet: "open(filename, mode)".


>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

Das erste Argument ist ein String mit dem Dateinamen. Das zweite Argument ist ein weiterer String, der ein paar Zeichen enthält, die beschreiben, wie die Datei verwendet werden soll. mode kann 'r' sein, wenn die Datei nur gelesen wird, 'w'um nur zu schreiben (eine bereits existierende Datei gleichen Namens wird überschrieben), und 'a' öffnet eine Datei, um Daten anzufügen; alle Daten, die in die Datei geschrieben werden, werden automatisch an das Ende angefügt. 'r+' öffnet eine Datei sowohl zum Lesen wie auch zum Schreiben. Das mode-Argument ist optional. Wenn es ausgelassen wird, wird 'r' angenommen.

Unter Windows und auf dem Macintosh, öffnet ein dem Modus angefügtes 'b' die Datei im Binär-Modus, d.h. es gibt auch Modi wie diese: 'rb', 'wb', und 'r+b'. Windows macht einen Unterschied zwischen Text- und Binär-Dateien; die Endzeilenmarkierung (EOL) in Text-Dateien werden automatisch leicht verändert, wenn Daten gelesen oder geschrieben werden. Diese Modifikation hinter den Kulissen ist in Ordnung für Text-Dateien in ASCII, aber sie wird Binär-Dateien wie JPEGs oder .EXE-Dateien korrumpieren. Seien Sie sehr vorsichtig, um auf jeden Fall den Binär-Modus für solche Dateien zu verwenden. (Man beachte, daß die exakte Semantik für den Text-Modus auf dem Macintosh von der verwendeten C-Bibliothek abhängig ist.)


7.2.1 Methoden von Datei-Objekten

Die restlichen Beispiele in diesem Abschnitt setzen voraus, daß eine Datei namens f bereits erzeugt wurde.

Um den Inhalt einer Datei zu lesen, rufen Sie f.read(size) auf, was eine gewisse Menge Daten liest und in einem String zurück gibt. size ist ein optionales numerisches Argument. Wird size weggelassen oder ist es negativ, so wird der gesamte Inhalt der Datei gelesen und zurück gegeben. Es ist Ihr Problem, wenn die Datei doppelt so groß ist wie Ihr Hauptspeicher. Anderenfalls werden höchstens size Bytes gelesen und zurück gegeben. Wird das Ende der Datei erreicht, so gibt f.read() einen leeren String zurück ().


>>> f.read()
'This is the entire file.\012'
>>> f.read()
''

f.readline() liest eine einzelne Zeile aus der Datei. Ein Zeilenvorschub-Zeichen (\n) wird am Ende des Strings stehengelassen es sei denn, es handelt sich um die letzte Zeile der Datei, wenn sie nicht mit einem Zeilenvorschub-Zeichen endet. Das macht den Rückgabewert eindeutig: wenn f.readline() einen leeren String zurück gibt, wurde das Ende der Datei erreicht, während ein Leerzeichen durch ein '\n' dargestellt wird, einem String, der nur ein einziges Zeilenvorschub-Zeichen enthält.


>>> f.readline()
'This is the first line of the file.\012'
>>> f.readline()
'Second line of the file\012'
>>> f.readline()
''

f.readlines() verwendet wiederholt f.readline() und gibt eine Liste mit allen Zeilen der Datei zurück.


>>> f.readlines()
['This is the first line of the file.\012', 'Second line of the file\012']

f.write(string) schreibt den Inhalt von string in die Datei und gibt None zurück.


>>> f.write('This is a test\n')

f.tell() gibt eine ganze Zahl zurück, die für die aktuelle Position in der Datei steht, gemessen in Bytes vom Anfang der Datei aus. Um die Position im Datei-Objekt zu verändern, verwende man "f.seek(offset, from_what)". Die Position wird berechnet, indem offset zu einem Bezugspunkt hinzu gezählt wird. Der Bezugspunkt wird durch das from_what-Argument ausgewählt. Ein Wert von 0 für from_what zählt vom Anfang der Datei, 1 von der aktuellen Position und 2 vom Ende der Datei aus. from_what kann weggelassen werden und wird zum Standardwert 0 ergänzt, was gleichbedeutend mit dem Anfang der Datei ist.


>>> f=open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Springe zum 5-ten Byte in der Datei.
>>> f.read(1)
'5'
>>> f.seek(-3, 2) # Springe zum 3-ten Byte vor dem Ende.
>>> f.read(1)
'd'

Wenn Sie die Datei nicht mehr brauchen, rufen Sie f.close() auf, um die Datei zu schließen und um etwaige Systemressourcen freizugeben, die die geöffnete Datei noch belegt. Nach f.close() werden alle weiteren Versuche, die Datei zu verwenden, automatisch scheitern:


>>> f.close()
>>> f.read()
Traceback (innermost last):
  File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

Datei-Objekte haben ein paar weitere Methoden, wie z.B. isatty() und truncate(), die seltener verwendet werden. Für eine vollständige Abhandlung von Datei-Objekten ziehe man die Bibliotheks-Referenz zu Rate.


7.2.2 Das pickle-Modul

Strings können sehr einfach in eine Datei geschrieben und von dort wieder gelesen werden. Für Zahlen muß man etwas mehr Aufwand treiben, da die read()-Methode nur Strings zurück gibt, die man einer Funktion wie string.atoi() übergeben muß, die dann einen String wie z.B. '123' nehmen und seinen numerischen Wert, 123, zurück geben. Wenn man jedoch komplexere Datentypen wie z.B. Listen, Klassen oder Instanzen abspeichern möchte, werden die Dinge sehr viel komplizierter.

Damit man nicht ständig Code zum Speichern komplexer Datenstrukturen schreiben und korrigieren muß, stellt Python ein Standard-Modul namens pickle zur Verfügung (engl. pickle = einlegen, haltbar machen). Dies ist ein erstaunliches Modul, das fast jedes Python-Objekt (selbst einige Formen von Python-Code!) in eine String-Repräsentation umwandeln kann. Dieser Prozeß wird (in Anlehnung an das Englische) Pickeln genannt. Die Rekonstruktion des Objektes aus der String-Repräsentation wird dual dazu Entpickeln genannt.7.1Zwischen dem Pickeln und Entpickeln könnte der String, der das Objekt repräsentiert, in einer Datei oder als Daten gespeichert oder über eine Netzwerkverbindung an einen entfernten Rechner geschickt worden sein.

Wenn Sie ein Objekt x, und ein zum Schreiben geöffnetes Datei-Objekt f haben, braucht es zur einfachsten Art des Pickelns nur eine Zeile Code:


pickle.dump(x, f)

Und um das Objekt wieder zu entpickeln (mit f als einem zum Lesen geöffneten Datei-Objekt):


x = pickle.load(f)

(Es gibt viele Varianten hiervon, je nachdem, ob man z.B. viele Objekte pickeln möchte oder die gepickelten Daten nicht in eine Datei schreiben möchte. Die vollständige Dokumentation für pickle findet man in der Bibliotheks-Referenz.

pickle ist der Standard-Weg, um Python-Objekte zu erzeugen, die gespeichert und von anderen Programmen oder vom gleichen Programm zu einem späteren Zeitpunkt wiederverwendet werden können. Der technische Ausdruck dafür ist ein persistentes Objekt. Weil die Verwendung von pickle so weitverbreitet ist, versuchen viele Autoren von Python-Erweiterungen sicherzustellen, daß neue Datentypen wie z.B. Matrizen sauber ge- und entpickelt werden können.



Fußnoten

... genannt.7.1
Das hat natürlich nichts mit Pickeln im deutschen Sprachgebrauch zu tun. [Der Übersetzer]


Send comments to python-docs@python.org.