Programmierkurs für absolute Anfänger Dateihandles, formatierte Ausgabe, Subroutinen Caren Brinckmann Sommersemester 2005 http://www.coli.uni-saarland.de/~cabr/teaching.php Dateihandles Um in Perl eine bestimmte Datei zum Lesen, Schreiben oder Anhängen zu öffnen, benötigt man so genannte Dateihandles. Ein Dateihandle ist der Name einer Eingabe-/Ausgabe-Verbindung zwischen dem Perl-Programm und der "Außenwelt". Dateihandles sollten komplett in Großbuchstaben geschrieben werden, damit sie nicht mit Funktionsnamen verwechselt werden können. Sechs Dateihandle-Namen sind bereits von Perl mit einer Bedeutung belegt (und sollten daher nicht für einen anderen Zweck benutzt werden): STDIN: Standardeingabe (normalerweise: Tastatur) STDOUT: Standardausgabe (normalerweise: Bildschirm) STDERR: Standardfehlerausgabe (normalerweise: Bildschirm) außerdem: ARGV, ARGVOUT, DATA Programmierkurs für absolute Anfänger – Sitzung 11 1 STDIN, STDOUT und STDERR Ein Perlprogramm liest normalerweise von STDIN und schreibt nach STDOUT (Funktion print). Fehlermeldungen (Funktion die) und Warnhinweise werden nach STDERR geschrieben. Normalerweise stellt STDIN eine Verbindung zwischen dem Perlprogramm und der Tastatur her, STDOUT und STDERR verbinden das Programm mit dem Bildschirm. Beim Programmaufruf kann man aber dafür sorgen, dass das Perlprogramm seine Eingabedaten aus einer Datei liest, seine Ausgabedaten in eine andere Datei schreibt, und die Fehlermeldungen an eine dritte Datei anhängt: perl -w meins.pl < rein.txt > raus.txt 2>> fehler.txt Programmierkurs für absolute Anfänger – Sitzung 11 2 Dateihandles öffnen: open Mit dem Operator open wird Perl angewiesen, eine Verbindung zwischen dem Programm und der Außenwelt (meistens: einer Datei) herzustellen. Dabei wird unterschieden, ob die entsprechende Datei zum Lesen, Schreiben oder Anhängen geöffnet wird: open(KIELDATEI, "<kkos025.s1h"); ## lesen open(AUSGABE, ">label.txt"); ## schreiben open(LOGDATEI, ">>log.txt"); ## anhängen Anstelle des Dateinamens kann eine skalare Variable verwendet werden, z.B.: $ausgabedatei = $ARGV[0]; open(AUSGABE, ">$ausgabedatei"); Programmierkurs für absolute Anfänger – Sitzung 11 3 Schlechte Dateihandles Wenn die Rechte der zu öffnenden Datei nicht korrekt gesetzt sind, ein falscher Dateiname angegeben wird oder andere Gründe gegen ein Öffnen sprechen, kann das Betriebssystem den Zugriff auf die Datei verweigern. Beim Versuch, von einem solchen schlechten Dateihandle zu lesen, wird undef bzw. die leere Liste zurückgegeben (je nach Kontext). Beim Versuch, in ein schlechtes Dateihandle zu schreiben, werden die Daten stillschweigend verworfen! Daher sollte immer überprüft werden, ob der Aufruf von open erfolgreich war. Ggf. wird das Programm mit einer entsprechenden Fehlermeldung abgebrochen: open(AUSGABE, ">$ausgabedatei") or die "Konnte Ausgabedatei $ausgabedatei nicht oeffnen: $!\n"; Programmierkurs für absolute Anfänger – Sitzung 11 4 Spezialvariable $! Nach einem fehlgeschlagenen Aufruf des Betriebssystems (z.B. durch den Versuch, eine Datei zu öffnen, für die der Benutzer keine Zugriffsrechte hat) enthält die Spezialvariable $! die Fehlermeldung des Betriebssystems, z.B. "Permission denied". Wird nach $! kein Zeilentrennzeichen \n eingefügt, so wird an das Ende der Meldung automatisch der Name des Perl-Programms und die Zeilennummer angehängt. Dies hilft bei der systematischen Fehlersuche. Beispiel: open(AUSGABE, ">$ausgabedatei") or die "Konnte Ausgabedatei $ausgabedatei nicht oeffnen: $!"; erzeugt z.B. folgende Fehlermeldung: Konnte Ausgabedatei test.txt nicht oeffnen: Permission denied at meinprogramm.pl line 4. Programmierkurs für absolute Anfänger – Sitzung 11 5 Dateihandles benutzen Wenn ein Dateihandle zum Lesen geöffnet ist, können die Zeilen genauso gelesen werden, als kämen sie von STDIN: open(KIELDATEI, "<$datei") or die "Konnte die Datei $datei nicht oeffnen: $!"; @zeilen = <KIELDATEI>; chomp(@zeilen); Standardverhalten von print: Ausgabe auf STDOUT. print kann aber auch mit einem Dateihandle als Argument aufgerufen werden, so dass die Ausgabe in die entsprechende Datei erfolgt: print LOGDATEI "Datei $datei bearbeitet.\n"; Programmierkurs für absolute Anfänger – Sitzung 11 6 Standarddateihandle für Ausgaben ändern: select print gibt die Daten standardmäßig nach STDOUT aus. Der Operator select setzt das Standarddateihandle für Ausgaben (mit print) auf das angegebene Dateihandle: open(AUSGABE, ">$ausgabedatei") or die "Konnte Ausgabedatei $ausgabedatei nicht oeffnen: $!"; select AUSGABE; print "Label: $label\tDauer: $dauer[$label]\n"; print "Ergebnis: $ergebnis\n"; Das mit select ausgewählte Standarddateihandle gilt bis zum Ende des Programms – oder bis es auf ein anderes Dateihandle gesetzt wird. Programmierkurs für absolute Anfänger – Sitzung 11 7 Dateihandles schließen: close Der Operator close schließt das angegebene Dateihandle und gibt die entsprechenden Systemressourcen wieder frei. Daher ist es guter Programmierstil, jedes geöffnete Dateihandle wieder zu schließen, sobald es nicht mehr gebraucht wird: open(KIELDATEI, "<$datei") or die "Konnte die Datei $datei nicht oeffnen: $!"; @zeilen = <KIELDATEI>; chomp(@zeilen); close KIELDATEI; Programmierkurs für absolute Anfänger – Sitzung 11 8 Dateigröße abfragen Wenn eine Datei existiert und nicht leer ist, kann man ihre Größe (in Bytes) mit dem Dateitest -s abfragen: $datei = "test.txt"; $dateigroesse = -s $datei; print "Die Datei $datei ist $dateigroesse Bytes groß.\n"; weitere Dateitests: http://perldoc.perl.org/functions/-X.html Programmierkurs für absolute Anfänger – Sitzung 11 9 Verzeichnishandles Wenn mehrere Dateien aus einem Verzeichnis gelesen werden sollen, so muss zunächst eine Liste der Dateinamen erzeugt werden. Dazu kann man ein Verzeichnishandle benutzen. Ein Verzeichnishandle sieht aus wie ein Dateihandle und verhält sich auch so: opendir öffnet ein Verzeichnishandle readdir liefert den nächsten Dateinamen aus dem Verzeichnis (skalarer Kontext) bzw. die Liste aller restlichen Dateinamen (Listenkontext) Achtung: Die Dateinamen enthalten nicht den Verzeichnisnamen! closedir schließt das Verzeichnishandle Programmierkurs für absolute Anfänger – Sitzung 11 10 Beispiel: Verzeichnishandles $verzeichnis = "/proj/PhonDat/Data/Read"; opendir(KIELVERZ, $verzeichnis) or die "Konnte Verzeichnis $verzeichnis nicht oeffnen: $!"; while ($dateiname = readdir KIELVERZ) { if ($dateiname =~ /\.s1h$/) { $dateiname = "$verzeichnis/$dateiname"; open(KIELDATEI, "<$dateiname") or die "Konnte Datei $dateiname nicht oeffnen: $!"; ## ... close KIELDATEI; } } closedir(KIELVERZ); Programmierkurs für absolute Anfänger – Sitzung 11 11 Formatierte Ausgabe: printf (1) Der Operator printf nimmt eine Formatierungszeichenkette, die das Format angibt, gefolgt von der Liste der Dinge, die ausgegeben werden sollen, und gibt diese auf STDOUT (oder einem anderen Dateihandle) aus. Die Formatierungszeichenkette enthält eine Reihe so genannter Konversionen: Jede Konversion beginnt mit einem Prozentzeichen und endet mit einem Buchstaben. Die Liste, die auf die Formatierungszeichenkette folgt, sollte immer genausoviele Elemente enthalten, wie es Konversionen gibt. printf "Das Passwort von %s ist noch %d Tage gültig!\n", $name, $gueltig; Die gebräuchlichsten Konversionen sind: %s %d %f eine Zeichenkette eine ganze Zahl eine Kommazahl (mit Punkt statt Komma) Programmierkurs für absolute Anfänger – Sitzung 11 12 Formatierte Ausgabe: printf (2) printf wird häufig dafür verwendet, Daten spaltenweise auszugeben, da für die meisten Konversionen eine Feldbreite angegeben werden kann. Beispiel: Zahlen rechtsbündig ausgeben (Spaltenbreite: 8 Zeichen) @zahlen = (23, 123778, -450); foreach $zahl (@zahlen) { printf "%8d\n", $zahl; } erzeugt folgende Ausgabe: 23 123778 -450 Ein negativer Wert für die Feldbreite führt zu einer linksbündigen Ausgabe. Programmierkurs für absolute Anfänger – Sitzung 11 13 Formatierte Ausgabe: printf (3) Mit der %f-Konversion kann man Kommazahlen runden: printf "%.3f\n", 6 * 7 + 2/3; printf "%.0f\n", 6 * 7 + 2/3; erzeugt folgende Ausgabe: 42.667 43 Kombination von Feldbreite und Rundung: printf "%8.3f\n", 6 * 7 + 2/3; printf "%8.0f\n", 6 * 7 + 2/3; erzeugt folgende Ausgabe: 42.667 43 Um ein Prozentzeichen auszugeben, muss %% verwendet werden: printf "Monatlicher Zinssatz: %.2f%%\n", 5.25/12; Programmierkurs für absolute Anfänger – Sitzung 11 14 Daten formatieren: sprintf sprintf nimmt die gleichen Argumente wie printf (abgesehen von dem optionalen Dateihandle). Die jeweilige Zeichenkette wird jedoch nicht ausgegeben, sondern als Rückgabewert zurückgegeben, der in einer skalaren Variablen gespeichert werden kann: $dauer = sprintf "%.0f", $dauer; Programmierkurs für absolute Anfänger – Sitzung 11 15 Subroutinen Wir kennen bereits diverse in Perl eingebaute Funktionen: chomp, reverse, die, pop, shift usw. Vom Programmierer selbst definierte Funktionen nennt man Subroutinen. Hierdurch kann man denselben Anweisungsblock in einem Programm mehrmals verwenden. Subroutinen können an jeder beliebigen Stelle im Programm definiert werden (Standard: entweder alle am Anfang oder alle am Ende). Subroutinen-Definitionen sind global, d.h. sie gelten im gesamten Programm. Achtung: Werden in einem Programm zwei Subroutinen mit dem gleichen Namen definiert, so überschreibt die später definierte die frühere! Programmierkurs für absolute Anfänger – Sitzung 11 16 Subroutinen: Definition und Aufruf Um eine Subroutine zu definieren, benutzt man das Schlüsselwort sub, gefolgt vom Namen der Subroutine und dem Anweisungsblock (in geschweiften Klammern): sub marine { $n += 1; ## globale Variable $n print "Hallo, Taucher Nummer $n!\n"; } Um eine Subroutine innerhalb des Programms aufzurufen, verwendet man einfach ihren Namen, dem ein & vorangestellt wird: $n = 0; &marine; &marine; &marine; &marine; Programmierkurs für absolute Anfänger – Sitzung 11 17 Subroutinen: Rückgabewerte (1) Oft ist es so, dass man beim Aufruf einer Subroutine das Ergebnis einer Berechnung in der Subroutine verwenden will, den so genannten Rückgabewert der Subroutine. In Perl haben alle Subroutinen automatisch einen Rückgabewert, nämlich den Wert des Ausdrucks, der zuletzt ausgewertet wird: sub summe_x_y { print "Aufruf der Subroutine summe_x_y\n"; $x + $y; # Dies ist der Rückgabewert. } $x = 3; $y = 4; $z = &summe_x_y; print "\$z hat den Wert $z.\n"; $k = 3 * &summe_x_y; print "\$k hat den Wert $k.\n"; Programmierkurs für absolute Anfänger – Sitzung 11 18 Subroutinen: Rückgabewerte (2) Achtung: Wenn die beiden Anweisungen in der Subroutine summe_x_y vertauscht werden, ist der Rückgabewert der Rückgabewert von print (normalerweise: 1)! sub summe_x_y { $x + $y; print "Aufruf der Subroutine summe_x_y\n"; } Wichtig: Der "letzte ausgewertete Ausdruck" ist nicht unbedingt identisch mit der letzten Zeile Text in der Subroutine: sub max_x_y { if ($x > $y) { $x; } else { $y; } } Programmierkurs für absolute Anfänger – Sitzung 11 19 Subroutinen: Rückgabewerte (3) Wird eine Subroutine im Listenkontext aufgerufen, so kann sie auch eine Liste von Werten zurückgeben: sub liste_von_x_bis_y { if ($x < $y) { $x..$y; } else { reverse $y..$x; } } $x = 11; $y = 6; @z = &liste_von_x_bis_y; print "@z"; Programmierkurs für absolute Anfänger – Sitzung 11 20 Subroutinen: Operator return Der Operator return beendet die Subroutine sofort und gibt das bei seinem Aufruf angegebene Argument zurück, z.B.: return $x + $y; Wird return ohne ein Argument benutzt, so wird ein leerer Wert zurückgegeben, also im skalaren Kontext: undef im Listenkontext: die leere Liste Guter Programmierstil: return jedesmal benutzen, wenn es einen sinnvollen Rückgabewert gibt. Programmierkurs für absolute Anfänger – Sitzung 11 21 Subroutinen: Argumente (1) In den bisherigen Beispielen wurden in den Subroutinen globale Variablen verwendet, d.h. Variablen, die von jedem Teil des Programms aus zugänglich und veränderbar sind. Z.B. hat die Subroutine max_x_y als Rückgabewert das Maximum der beiden globalen Variablen $x und $y: sub max_x_y { if ($x > $y) { return $x; } else { return $y; } } Viel praktischer ist es jedoch, die Subroutine bei jedem Aufruf mit neuen Werten – so genannten Argumenten – aufrufen zu können, ohne sich auf die globalen Variablen $x und $y verlassen zu müssen. Programmierkurs für absolute Anfänger – Sitzung 11 22 Subroutinen: Argumente (2) Um eine Liste von Argumenten an eine Subroutine zu übergeben, werden diese einfach in runden Klammern hin der Aufruf der Subroutine geschrieben: $n = &max(10, 15); Für die Dauer der Subroutine enthält die spezielle Arrayvariable @_ die Liste der aufrufenden Argumente. Im obigen Beispiel enthält beim Aufruf der Subroutine max also $_[0] den Wert 10, $_[1] enthält den Wert 15. Diese Werte kann die Subroutine max dann verwenden. Die Variable @_ existiert in der Subroutine lokal, d.h. wenn es auch eine globale Variable @_ gibt, so wird deren Wert vor dem Aufruf der Subroutine gesichert und nach Beendung der Subroutine wieder hergestellt. Bei verschachtelten Subroutinen-Aufrufen bekommt so jede Subroutine ihr eigenes @_-Array zugeteilt. Programmierkurs für absolute Anfänger – Sitzung 11 23 Private Variablen (1) Standardmäßig sind alle Variablen in einem Perl-Programm als globale Variablen angelegt, d.h. sie sind von jedem Teil des Programms aus zugänglich und veränderbar. Mit dem Operator my können aber private bzw. lexikalische Variablen angelegt werden: sub max { my ($x, $y) = @_; if ($x > $y) { return $x; } else { return $y; } } Private Variablen gelten nur innerhalb des sie umschließenden Anweisungsblocks: Andere Variablen außerhalb des Blocks, die auch $x und $y heißen, werden nicht beeinflusst. Ebenso können die privaten Variablen nicht versehentlich von außen verändert werden. Die obige Subroutine max kann also in ein beliebiges Perl-Programm eingebaut werden, ohne andere Variablen in dem Programm, die zufällig auch $x und $y heißen, zu verändern. Programmierkurs für absolute Anfänger – Sitzung 11 24 Private Variablen (2) Private Variablen können in jedem beliebigen Anweisungsblock verwendet werden, z.B. in einem if-Block oder in Schleifen mit for oder while: @zahlen = 2..5; for (my $i=0; $i <= $#zahlen; $i++) { my $quadrat = $zahlen[$i] ** 2; print "$zahlen[$i] zum Quadrat ist $quadrat.\n"; } print "\$i hat den Wert $i.\n"; Mit my lassen sich auch neue private Arrays oder Hashes anlegen: my @zahlenliste; my %telefonnummer; Neu angelegte Variablen sind zu Beginn immer leer (es sei denn, sie werden mit einem bestimmten Wert initialisiert), d.h. undef für skalare Variablen und die leere Liste für Arrays und Hashes. Programmierkurs für absolute Anfänger – Sitzung 11 25 Private Variablen (3) Achtung: Der Operator my verändert den Kontext einer Zuweisung nicht! skalarer Kontext: my $num = @_; $num enthält als Wert die Anzahl der Elemente in @_ Listenkontext: my ($num) = @_; $num enthält als Wert das erste Element von @_ Programmierkurs für absolute Anfänger – Sitzung 11 26 Pragma "use strict" Ein Pragma ist eine Anweisung an den Compiler, wie das Programm kompiliert werden soll. Diese Anweisung wird an den Anfang des Programms geschrieben. Das Pragma use strict teilt dem internen Perl-Compiler mit, dass innerhalb des Programms einige gute Programmierregeln beachtet werden müssen, u.a. dass jede Variable mit my deklariert werden muss. Ausnahme: Die in Perl eingebauten Variablen wie z.B. $_, @_, $!, @ARGV, $a und $b (bei sort) und werden niemals extra mit my deklariert. Damit gibt es schon eine Fehlermeldung zur Compile-Zeit, wenn z.B. eine Variable aus Versehen falsch geschrieben wurde. Regel: Bei jedem Programm, das mehr als 20 Zeilen lang ist, use strict verwenden und alle Variablen mit my deklarieren! Programmierkurs für absolute Anfänger – Sitzung 11 27 Beispiel: use strict use strict; my @woerter = <STDIN>; chomp (@woerter); ## ... weitere Anweisungen if ($wort[0] eq "hend") { ## ... weitere Anweisungen } Fehlermeldung zur Compile-Zeit, da keine Arrayvariable mit dem Namen @wort deklariert wurde. Das Programm wird nicht ausgeführt. Global symbol "@wort" requires explicit package name at stricttest.pl line 5. Execution of stricttest.pl aborted due to compilation errors. Programmierkurs für absolute Anfänger – Sitzung 11 28 Argumentlisten mit variabler Länge Innerhalb einer Subroutine kann leicht überprüft werden, ob die korrekte Anzahl von Argumenten übergeben wurde: sub max { my ($x, $y) = @_; if (@_ != 2) { print "WARNUNG! &max braucht genau 2 Argumente!\n"; } if ($x > $y) { return $x; } else { return $y; } } Allerdings ist es besser, die Subroutine max so umzuschreiben, dass sie mit einer beliebigen Anzahl von Argumenten umgehen kann: sub max { my ($max_aktuell) = shift @_; foreach (@_) { if ($_ > $max_aktuell) { $max_aktuell = $_; } } return $max_aktuell; } Programmierkurs für absolute Anfänger – Sitzung 11 29 Leere Argumentlisten Angenommen, die neue Subroutine max wird im Programm folgendermaßen aufgerufen: @zahlen = <STDIN>; chomp @zahlen; $maximum = &max(@zahlen); Was passiert, wenn @zahlen die leere Liste enthält, weil die eingelesene Datei leer ist? Die Subroutine max wird mit einer leeren Liste als Argument aufgerufen. $max_aktuell erhält den Wert undef, und die foreachSchleife wird keinmal ausgeführt. Damit ist der Rückgabewert undef. Programmierkurs für absolute Anfänger – Sitzung 11 30 Beispiel: Indexbestimmung use strict; my @namen = ("Fred", "Otto", "Betty", "Dino", "Paul"); my $index = &welchen_index_hat("Dino", @namen); print "index von Dino: $index\n"; sub welchen_index_hat { my ($element, @liste) = @_; foreach my $i (0..$#liste) { if ($element eq $liste[$i]) { return $i; } } return -1; } Programmierkurs für absolute Anfänger – Sitzung 11 31 Beispiel: Subroutine summe Die Subroutine summe erhält als Argument eine Liste von Zahlen und gibt die Summe dieser Zahlen zurück. my @zahlen = 1..1000; my $summe = &summe(@zahlen); print "Die Summe der Zahlen von 1 bis 1000 ist $summe.\n"; sub summe { my $summe; foreach (@_) { $summe += $_; } return $summe; } Programmierkurs für absolute Anfänger – Sitzung 11 32