Perl TutorialDies ist eine Bearbeitung des Perl Tutorials von Nik Silver ( School of Computer Studies, University of Leeds GB). Das Ziel dieses Tutorials ist es, die Grundlagen von Perl zu erarbeiten, um effizient CGI-Scripts zu schreiben. Dabei geht es nicht darum, die Programmiersprache Perl erschöpfend zu behandeln, sondern um den Einstieg in weitere eigene Bemühungen zu schaffen. Diese HTML-Version ist als tar.gz - File verfügbar. Daneben gibt es für Perl V5 die Manpages in HTML-Format. oder ebenfalls tar.gz - File. Musterlösungen! Es kann schon mal vorkommen, dass dieser Link ins Leere zeigt. Dann gibt's nur eines: später probieren! Die folgende Themen werden in diesem Tutorial behandelt:
Das erste ProgrammHier ist das erste Programm um uns anzuwärmen. #!/usr/local/bin/perl # # Das unvermeidliche erste Programm # print 'Hello world.'; # Ausgabe eines Textes Im folgenden werden wir jeden Teil dieses Programmes besprechen. Die erste ZeileJedes Perl Programm beginnt mit einer solchen Zeile, #!/usr/local/bin/perl obschon sie von System zu System unterschiedlich aussehen kann. Diese Zeile sagt dem Computer, was er bei der Ausführung zu tun hat. (In diesem Fall soll er es durch den Perl-Interpreter schicken.) Kommentare und AnweisungenDas # Symbol eröffnet einen Kommentar. Alles zwischen # und dem Zeilenende wird vom Interpreter ignoriert (Ausnahme: erste Zeile). Kommentare können überall im Programm verwendet werden. Der einzige Weg um Kommentare über mehrere Zeilen ausdehnen zu können, ist die Verwendung von # in jeder Zeile. Alles übrige sind Perl-Anweisungen, welche mit einem Strichpunkt beendet werden müssen, wie die letzte Zeile oben. Einfache AusgabeDie print Funktion gibt Information aus. Im obigen Fall gibt sie den String Hello world. aus. Und selbstverständlich endet die Anweisung mit einem Strichpunkt. Sie werden feststellen, das obiges Programm ein nicht ganz erwartetes Resultat erzeugt. Wir werden es deshalb als nächstes laufen lassen.
Ein Programm ausführenSchreibe das Beispielprogramm mit einem Texteditor und speichere es ab. (Emacs hat einen Perl-Modus 'M-x perl-mode'!) Danach muss es ausführbar gemacht werden mit chmod u+x progname als UNIX-Kommandozeile, wobei progname der Name Programmfiles ist. Um das Programm zu starten, muss eine der folgenden Zeilen eingegeben werden: . perl progname ./progname progname Vielleicht haben Sie Glück und das Programm wird ausgeführt. Möglicherweise erscheinen aber Fehlermeldungen, oder es geschieht überhaupt nichts. Man kann mit folgendem Befehl perl veranlassen, Warnungen und weitere hilfreiche Meldungen auszugeben, bevor das Programm ausgeführt wird: perl -w progname Perl hat auch einen Debugger. Er wird mit perl -d progname gestartet. Bei der Ausführung eines Perl-Programmes (Perl-Script) wird der Text zuerst 'kompiliert' (interpretiert). Dieser Schritt erzeugt auch die Fehlermeldungen. Anschliessend wird es ausgeführt. Die eigentliche Laufzeit ist vergleichbar mit einem C-Programm. Je nach Länge des Programmes, muss mit einer gewissen 'Kompilierzeit' gerechnet werden, welche nur mit speziellen Hilfsmitteln (undump) ausgeschaltet werden kann. Bevor wir weitermachen, sollte das Programm laufen. Der Output entspricht vielleicht nicht ganz den Erwartungen, mindestens ist er nicht so schön. Zunächst wenden wir uns den Variablen zu und danach können wir auch die Ausgaben verbessern.
Skalare VariablenDie elementarsten Variablen in Perl sind die skalaren Variablen. Skalare Variablen können Zahlen und Strings beinhalten und, je nach Kontext, werden sie als Zahlen oder Strings interpretiert. Vor dem eigentlichen Namen muss immer ein $ -Zeichen stehen. Im folgenden Beispiel $priority = 9; wird der Variablen $priority den Wert 9 zugeordnet, es ist aber auch möglich der genau gleichen Variablen einen String zuzuordnen: $priority = 'high'; Perl akzeptiert auch Zahlen als Strings: $priority = '9'; $default = '0009'; Damit können problemlos auch arithmetische Operationen ausgeführt werden. Variablennamen können aus Buchstaben, Zahlen und Unterstrich zusammengesetzt werden. Sie sollten jedoch nicht mit einer Zahl beginnen und die Variable $_ hat eine spezielle Bedeutung. Perl unterscheidet zwischen Gross- und Kleinschreibung, dh. $a und $A sind verschieden. Operationen und ZuweisungenPerl verwendet alle gebräuchlichen C-Operatoren. $a = 1 + 2; # Addiere 1 und 2 und speichere Resultat in $a $a = 3 - 4; # Subtrahiere 4 von 3 ... $a = 5 * 6; # Multipiziere 5 und 6 $a = 7 / 8; # Dividiere 7 mit 8 ($a = 0.875) $a = 9 ** 10; # Neun hoch zehn $a = 5 % 2; # % MOD 2 ++$a; # Inkrement $a, Rückgabe von $a $a++; # Rückgabe von $a, Inkrement $a --$a; # Dekrement $a, Rückgabe von $a $a--; # Rückgabevon $a, Dekrement $a Für Strings gibt es unter anderem: $a = $b . $c; # Konkateniere $b und $c $a = $b x $c; # Füge $b $c-mal zusammen Zuweisungen: $a = $b; # $a wird $b $a += $b; # $a wird um $b vergrössert $a -= $b; # $a wird um $b verkleinert $a .= $b; # $a wird mit $b konkateniert Bei der Zuweisung $a = $b macht Perl eine Kopie von $b und weist diese $a zu. Eine darauffolgende Aenderung von $b ändert $a nicht. Weitere Operatoren sind im Manual beschrieben. Zugang zum Manual erhält man auch mit man perlop. SubstitutionFolgende Programmzeilen sollen apples and pears ausgeben. Wir wollen den Operator für Konkatenation verwenden: $a = 'apples'; $b = 'pears'; print $a.' and '.$b; Es wäre schöner, nur einen String im print statement zu verwenden, aber mit print '$a and $b'; erhalten wir genau $a and $b, was nicht unserem Wunsch entspricht. Die Verwendung von doppelten Anführungszeichen veranlasst Perl die Variablen aufzulösen (engl. interpolation): print "$a and $b"; Daneben werden auch Spezialzeichen, wie \n und \t aufgelöst. \n ist ein Zeilenumbruch und \t ist ein Tabulator. UebungWir schreiben das Hello-World-Programm um, sodass (a) der String einer Variablen zugeordnet wird und (b) diese Variable mit anschliessendem Zeilenumbruch ausgegeben wird. Wir benützen doppelte Anführungszeichen und nicht den Operator für Konkatenation.
Array VariablenEine weitere Art von Variablen sind die Array Variablen. Sie stellen eine Liste von skalaren Variablen dar (ie. Zahlen und Strings). Die Namen von Array-Variablen haben dasselbe Format, wie die skalaren Variablen, ausser das sie anstelle eines $-Symbols, am Anfang ein @-Symbol brauchen. Beispiel: @food = ('apples', 'pears', 'eels'); @music = ('whistle', 'flute'); Damit werden der Variablen @food eine Liste von drei Elementen, der Liste @music eine Liste von zwei Elementen zugewiesen. Die Array-Elemente werden mittels Indizes in eckigen Klammern zugegriffen. Der Index beginnt bei 0. Der Wert von $food[2] ist somit eels. Beachte, dass nicht mehr @, sondern $ am Anfang des Variablennamens steht. Ein Element eines Arrays ist ein Skalar! Array ZuweisungWie alles in Perl kann der gleiche Ausdruck in unterschiedlichem Kontext ein anderes Resultat erzeugen. Die erste Zuweisung unten ergänzt den Array @music. Somit sind beide Zuweisungen äquivalent. @moremusic = ('organ', @music, 'harp'); @moremusic = ('organ', 'whistle', 'flute', 'harp'); Damit können Elemente zu einem Array hinzugefügt werden. Ein anderer Weg um Elemente hinzuzufügen sieht folgendermassen aus: push(@food, 'eggs'); Damit wird das Element eggs ans Ende des Arrays @food angehängt. Man kann auch mehrere Elemente auf's Mal anhängen: push(@food, 'eggs', 'lard'); push(@food, ('eggs', 'lard')); push(@food, @morefood); Die push-Funktion hat als Rückgabewert die neue Länge des Arrays. Analog kann mit pop das letzte Element von einer Liste entfernt werden. pop entfernt von der ursprünglichen Liste @food das letzte Element eels. Der Rückgabewert ist das entfernte Element. @food hat nun noch zwei Elemente: $grub = pop(@food); # Jetzt ist $grub = 'eels' (grub=Frass) Es ist auch möglich, einem Array eine skalare Variable zuzuordnen. Wie immer ist der Kontext wichtig. Die Zeile $f = @food; weist $f die Länge des Arrays $food zu. Hingegen wird mit $f = "@food"; die Liste in einen String konvertiert, welcher zwischen den Elementen je ein Leerzeichen hat. Mit der Spezialvariablen $" kann das Leerzeichen durch einen beliebigen anderen String ersetzt werden. Diese Variable ist eine von vielen Spezialvariablen, mit welchen das Verhalten von Perl gesteuert werden kann. Arrays können auch verwendet werden, um gleichzeitig mehrere Zuordnungen von skalaren Variablen zu machen: ($a, $b) = ($c, $d); # Aequvalent $a=$c; $b=$d; ($a, $b) = @food; # $a und $b sind die ersten # beiden Elemente von @food. ($a, @somefood) = @food; # $a ist das erste El. von @food # @somefood sind die übrigen (@somefood, $a) = @food; # @somefood ist @food und # $a ist nicht definiert. Zur letzten Zuweisung ist zu bemerken, dass Arrays alle Elemente, welche sie antreffen, kopieren werden. Damit ist diese letzte Form nicht sehr nützlich. Zum Schluss möchten wir noch die Länge eines Arrays bestimmen können. Der Ausdruck $#food ergibt den Index des letzten Elementes eines Arrays. Sofern wir mit dem Index 0 begonnen haben (Achtung: Spezialvariable $[) wird die Länge von @food: $len=$#food+1; oder im allgemeinen: $len=$#food+1-$[; Arrays ausgebenWeil der Kontext wichtig ist, ist es nicht erstaunlich, dass die folgenden Ausdrücke alle unterschiedliche Resultate erzeugen: print @food; # print "@food"; # mit doppelten Anführungszeichen print @food.""; # in skalarem Kontext (. Konkat.) UebungWir probieren die obigen print-Statements aus und schauen, was dabei herauskommt. Man darf es sich auch vorher überlegen!
Hashes (Assoziative Arrays)Normale Arrays oder Listen erlauben den Zugriff zu den Elementen über die Elementnummern. Das erste Element des Arrays @food ist $food[0]. Das zweite Element ist $food[1], und so weiter. Mit Perl können aber auch Arrays erzeugt werden, deren Elemente durch Strings referenziert werden. Diese Arrays nennt man assoziative Arrays oder hashes (die keyword - value -Paare werden in Hash-Tabellen abgelegt.) Um einen Hash zu definieren verwenden wir die normale Klammer-Notation. Der Arrayname hat jedoch ein vorangestelles %-Zeichen (im Gegensatz zum @-Zeichen des gewöhnlichen Arrays). Wir wollen einen Hash definieren, welcher den Namen und das Alter von Personen enthält. Das sieht folgendermassen aus: %ages = ('Michael Caine', 39, 'Dirty Den', 34, 'Angie', 27, 'Willy', '21 in dog years', 'The Queen Mother', 108); Beachte: In Perl V5 können die Kommas durch => -Operator ersetzt werden. Damit wird die Zusammengehörigkeit der Schlüssel-Wert-Paare dokumentiert und er bewirkt, dass der Wert links des Operators als String interpretiert wird. Obiges kann deshalb auch folgendermassen geschrieben werden: %ages = ('Michael Caine' => 39, 'Dirty Den' => 34, Angie => 27, Willy => '21 in dog years', 'The Queen Mother', 108); Das Alter der Leute kann mit den folgenden Anweisungen gefunden werden: $ages{'Michael Caine'}; # Ergibt 39 $ages{'Dirty Den'}; # Ergibt 34 $ages{Angie}; # Ergibt 27 $ages{Willy}; # Ergibt "21 in dog years" $ages{'The Queen Mother'}; # Ergibt 108 Und mit $ages{'ich'} = 'jung'; kann man beliebige weitere Elemente hinzufügen. Das %-Zeichen wird wiederum durch ein $-Zeichen ersetzt, falls ein einzelnes Element gemeint ist. Ein Element ist ein Skalar! Im Unterschied zu den normalen Arrays, wo der Index in eckigen Klammern geschrieben wird, verwenden wir bei den Hashes die geschweiften Klammern. Der Index ist ein String, eben der Name der Person. Ein Hash kann in einen normalen Array umgewandelt werden, indem er einfach einem normalen Array zugewiesen wird. Umgekehrt wird ein normaler Array in einen Hash umgewandelt. Idealerweise hat der normale Array eine gerade Anzahl Elemente: @info = %ages; # @info ist ein normaler Array. # Er hat jetzt 10 Elemente. $info[5]; # Das 5. Element von @info # den Wert 27 %moreages = @info; # %moreages ist ein assoziativer # Array. Es ist der gleiche wie %ages OperatorenDie Elemente von assoziative Arrays sind ungeordnet (hash-tables) aber man kann die Elemente der Reihe nach mit den Funktionen keys und values zugreifen: foreach $person (keys %ages) { print "Ich kenne das Alter von $person.\n"; } foreach $age (values %ages) { print "Jemand ist $age Jahre alt.\n"; } Die Funktion keys erzeugt ein Liste der Schlüsselwörter (das sind die Indizes)
des Hash. Die Funktion values erzeugt ein Liste der Werte. Die Reihenfolge der
beiden Listen ist gleich, sie hat jedoch nichts mit der Reihenfolge der Eingabe zu tun. Daneben gibt es noch eine Funktion each, welche einen Array mit zwei Elementen erzeugt, dem Schlüsselwort und dem Wert. Aufeinanderfolgende Aufrufe von each geben immer ein neues key/value-Paar aus, solange solche vorhanden sind: while (($person, $age) = each(%ages)) { print "$person ist $age Jahre alt\n"; } Umgebungs-VariablenUNIX kennt das Konzept von Umgebungsvariablen, welche es erlauben, Informationen über das System interessierten Programmen weiterzugeben. Zum Beispiel wird in der USER-Variablen den Namen des eingeloggten Benutzers gespeichert und in der HOME-Variablen dessen Home-Directory, usf. Perl stellt diese Variablen in dem assoziativen Array %ENV zur Verfügung. Die Schlüsselwörter dieses Arrays sind die Namen der Umgebungsvariablen. Somit wird das folgende Programmstück die aktuellen Werte der Umgebungsvariablen USER und HOME ausgeben: print "Du bist User $ENV{'USER'} mit "; print "Homedirectory $ENV{'HOME'}\n"; FilesDas folgende Perlprogramm liest ein File und gibt den Inhalt des Files aus. #!/usr/local/bin/perl # # Program to open the password file, read it in, # print it, and close it again. $file = '/etc/passwd'; # Filename zuweisen open(INFO, $file); # File öffnen @lines = <INFO>; # in Array einlesen close(INFO); # File schliessen print @lines; # Array ausgeben Die Funktion open öffnet ein File zum lesen. Der erste Parameter ist ein Filehandle, welcher es erlaubt, das File in Zukunft zu referenzieren. Der zweite Parameter ist ein Ausdruck, der den Filenamen bezeichnet. Die Funktion close schliesst das File. Selbstverständlich möchten wir nicht nur Files lesen, sondern auch schreiben oder etwas zu einem File hinzufügen können. Die Verwendung des richtigen Prefix zum Filenamen der Funktion open erlaubt diese Dinge: open(INFO, $file); # Lesen open(INFO, ">$file"); # Schreiben open(INFO, ">>$file"); # Anhängen open(INFO, "<$file"); # Lesen Um etwas in ein geöffnetes File zu schreiben, verwenden wir die Funktion print mit einem zusätzlichen Parameter, dem Filehandle: print INFO "This line goes to the file.\n"; Dieser Ausdruck schreibt in File mit Filehandle INFO obiger Text zwischen Hochkommas. Beachte: Es darf kein Komma nach INFO stehen! Zwecks besserer Lesbarkeit werden Filehandles mit Grossbuchstaben bezeichnet. Im obigen Programm wird ein File eingelesen. Der Filehandle ist INFO und um es einzulesen verwendet man spitze Klammern. Der Ausdruck @lines = <INFO>; liest das File in den Array @lines. Beachte, dass das gesamte File auf's Mal eingelesen wird. Der Grund liegt im Array-Kontext. Falls @lines durch die skalare Variable $line ersetzt würde, würde nur eine Zeile eingelesen und beim nächsten Aufruf die nächste. $line speichert die ganze Zeile inklusive newline-character. Die Filehandles STDIN, STDOUT und STDERR sind vordefiniert. Defaultmässig schreibt print auf STDOUT und <> liest von STDIN ( <> ist äquivalent zu <STDIN>). UebungWir ändern das obige Programm, sodass das ganze File mit einem # Symbol am Anfang jeder Zeile ausgegeben wird. Dazu sollte nur eine Zeile zum Programm hinzugefügt und eine weitere geändert werden müssen. Wir verwenden dazu die Spezialvariable $". Mit Files können unvorhergesehene Dinge geschehen, deshalb ist es nützlich, die -w-Option zu verwenden, welche wir im Abschnitt Ein Programm ausführen erwähnt haben.
KontrollstrukturenWeitere interessante Möglichkeiten werden mit Kontrollstrukturen und Schleifen eröffnet. Perl unterstützt viele verschiedene Arten von Kontrollstrukturen, welche zum Teil C ähnlich sehen, aber auch an Pascal erinnern. Wir werden hier ein paar davon ansehen. foreachUm jedes Element eines Arrays oder einer andern Listenstruktur (wie zun Beispiel die Zeilen eines Files) zu durchlaufen, kann foreach verwendet werden. Das sieht folgendermassen aus: foreach $morsel (@food) # Gehe der Reihe nach durch # @food und nenne das # Element $morsel { print "$morsel\n"; # Ausgabe des Elementes print "Yum yum\n"; # That was nice } Die Anweisungen, welche jedesmal durchgeführt werden sollen, müssen in geschweiften Klammern angegeben werden. Falls @food leer wäre, würde der Block in geschweiften Klammern nie durchlaufen. Bool'sche OperatorenDie nächsten Anweisungen testen die Gleichheit zweier Ausdrücke. In Perl wird jede Zahl ungleich Null und jeder nicht leere String als true betrachtet. Die Zahl Null, Null als String und der leere String sind false. Hier sind ein paar Tests auf Gleichheit für Zahlen und Strings: $a == $b # $a numerisch gleich $b? # Achtung: Nicht = verwenden $a != $b # $a numerically ungleich $b? $a eq $b # $a gleicher String wie $b? $a ne $b # $a und $b nicht der gleiche String? Es gibt auch die logischen AND, OR und NOT: ($a && $b) # ist $a und $b true? ($a || $b) # ist entweder $a oder $b true? !($a) # ist $a false? forDie for-Struktur von Perl ist vergleichbar mit C: for (init; test; inkr) { first_action; second_action; etc } Zuerst wird init ausgeführt. Falls test true ist, wird der Block von Anweisungen in geschweiften Klammern ausgeführt. Nach jedem Durchgang wird inkr ausgeführt. Mit der folgenden for-Schleife werden die Zahlen 1 bis 9 ausgegeben: for ($i = 0; $i < 10; ++$i) # Starte mit $i = 1 # falls $i < 10 # Inkr. $i vor Block { print "$i\n"; } Die reservierten Wörter for und foreach können für beide Strukturen verwendet werden. Perl merkt dann schon, was gemeint ist! while und untilDas folgende Programm liest eine Eingabe von Keyboard und wiederholt die Anfrage, bis das korrekte Passwort eingegeben wurde. #!/usr/local/bin/perl print "Password? "; # Fragt nach Input $a = <STDIN>; # liest input chop $a; # löscht \n am Ende while ($a ne "fred") # Solange $a falsch ist { print "sorry. Again? "; # Fragt wieder $a = <STDIN>; # liest chop $a; # löscht \n am Ende } Die while-Schleife sollte soweit klar sein. Wie immer haben wir einen Block von Anweisungen in geschweiften Klammern. Ein paar Dinge können wir hier noch erwähnen: Erstens können wir von STDIN lesen, ohne es zuerst geöffnet zu haben. Zweitens wird mit der Eingabe des Passwortes auch ein newline-character in $a abgespeichert. Drittens ist die chop-Function dazu da, den letzten Buchstaben eines Strings abzuschneiden, in diesem Fall den newline-character. Um das Gegenteil zu testen, können wir die until-Schleife in genau gleicher Weise verwenden. Damit wird der Block solange ausgeführt, bis der Test true ist und nicht solange er true ist. Eine andere Art vorzugehen, ist die Verwendung des while- oder until-Tests am Ende des Blocks anstatt am Anfang. Dazu benötigt man noch den do-Operator, welcher den Blockanfang markiert. Falls wir die Meldung sorry. Again weglassen, könnten wir das Passwortprogramm folgendermassen schreiben: #!/usr/local/bin/perl do { print "Password? "; # Fragt nach Input $a = <STDIN>; # liest Input chop $a; # löscht \n } while ($a ne "fred"); # Nochmals solange falscher Input UebungWir ändern das Programm der letzten Uebung, indem jede Zeile einzeln eingelesen werden soll und die Ausgabe am Anfang die Zeilennummer aufweist. Die folgende Struktur ist vielleicht hilfreich: while ($line = <INFO>) { ... } Danach hätten wir noch gerne die Zeilennummern im folgenden Format: 001, 002, ..., 009, 010, 011, 012., etc. Um das zu erhalten, braucht es nur einen Zusatz von vier Buchstaben in einer Zeile. Perl ist da sehr flexibel...
BedingungenNatürlich kennt Perl auch if/then/else-Anweisungen. Sie sehen wie folgt aus: if ($a) { print "The string is not empty\n"; } else { print "The string is empty\n"; } Erinnern wir uns, das ein String als false betrachtet wird, falls er leer ist, sowie wenn $a gleich 0 ist. Es gibt aber noch mehr Möglichkeiten: if (!$a) { # Das ! ist der NOT Operator print "The string is empty\n"; } elsif (length($a) == 1) { # falls obiges false print "The string has one character\n"; } elsif (length($a) == 2) { # falls obiges false print "The string has two characters\n"; } else { # andernfalls print "The string has lots of characters\n"; } Beachte: bei elsif muss tatsächlich das 'e' fehlen. UebungWir nehmen ein ziemlich langes File, welches Text und Leerzeilen enthält. Zum Beispiel gibt es folgende Geschichte aus dem Tages-Anzeiger. Von der letzten Uebung haben wir ein Programm, welches das Passwort-File mit Zeilennummerierung ausgibt. Aendere dieses Programm, sodass die Zeilennummern bei Leerzeilen nicht mitgerechnet und ausgegeben werden, jedoch die Zeile selber immer ausgegeben wird. Beachte, dass beim Einlesen eines Files, jede Zeile am Ende den newline-Character enthält.
MustererkennungEine der nützlichsten Eigenschaften von Perl (wenn nicht die nützlichste Eigenschaft) ist das mächtige String-Handling. Und der Motor des String-Handling sind die regulären Ausdrücke (RA) zur Mustererkennung, welche auch von vielen UNIX Hilfsprogrammen verwendet werden. Reguläre AusdrückeEin regulärer Ausdruck steht zwischen zwei Schrägstrichen '/' und der Mustererkennungs-Operator ist =~. Der folgende Ausdruck ist true, falls der String (oder das Muster, oder der reguläre Ausdruck) das in der Variablen $satz vorkommt. $satz =~ /das/; Effektiv ist der "generische Musteroperator" m//. Die Schrägstriche können mit der m-Notation durch ein beliebiges anderes Zeichen ersetzt werden. . $satz =~ m:das:; Ohne m-Notation braucht es die Schrägstriche. Das ist die Schreibweise, die man aus historischen Gründen auch fast immer sieht. Der RA unterscheidet Gross- und Kleinschreibung, dh. mit $satz = "Das Muster kommt vor!"; wird der obige Vergleich false. Der Operator !~ macht das Gegenteil. Mit obiger Zuweisung wird deshalb der Ausdruck $satz !~ /das/ true, da der String das in $satz nicht auftritt. Die Defaultvariable $_Wir können die folgende Bedingung verwenden if ($satz =~ /uster/) { print "Das Muster \'uster\' kommt vor.\n"; } welche eine Nachricht ausgibt, falls wir einen der folgenden Sätze hätten: $satz = "Ein Muster ohne Wert"; $satz = "Uster mustern"; Häufig ist es einfacher, den Satz der Defaultvariablen $_ zuzuordnen, welche natürlich skalar ist. Damit können wir die Verwendung der Mustererkennungs-Operatoren umgehen und einfach schreiben: if (/uster/) { print "Wir sprechen von Mustern\n"; } Die $_-Variable, ist die Default-Variable für viele Perl-Operationen und -Funktionen und wird sehr häufig gebraucht. Mehr über RAsRA können aber sehr viel mehr als reiner Vergleich von Zeichenketten. Es gibt viele Spezialzeichen, die eine bestimmte Bedeutung haben. Mit diesen Spezialzeichen wird erst die volle Funktionalität (und auch die Komplexität) der RA's erreicht. Wir empfehlen, mit einfachen RA's zu beginnen und sich langsam zu steigern. Die hohe Schule der RA's braucht Erfahrung und Kreativität. Als Einstieg schauen wir uns ein paar einfache Beispiele an. Nehmen wir an, wir hätten einen String und möchten wissen, ob darin die Zeichenfolge ac vorkommt. Der RA dazu ist /ac/. Anstelle von ac möchten wir auch noch bc zulassen, dann heisst der RA /[ab]c/. Falls nun mehrere a's und b's vor dem c erlaubt sein sollen, können wir schreiben: /[ab]+c/. Folgende Menge von Strings sind damit erlaubt: ac bc aac bc abc bac aaac und so weiter. Schreiben wir anstelle des +-Zeichens ein *
wäre auch ein c alleine erlaubt. Wenn wir am Anfang sicher ein a
wollen, müssen wir /a[ab]*c/ schreiben. Welche Strings aus der obigen
Liste werden damit erkannt? Verlassen wir unser Beispiel und schauen uns weitere Spezialzeichen an: . # Ein einzelner Buchstaben ohne newline ^ # Zeilen- oder Stringanfang $ # Zeilen- oder Stringende * # Null oder mehrere Male den letzten Buchstaben, greedy! *? # ditto, aber minimal + # Ein oder mehrere Male den letzten Buchstaben ? # Null oder ein Mal den letzten Buchstaben und dazu auch gleich ein paar weitere Beispiele. Wir erinnern uns daran, das ein RA zwischen /.../ stehen soll. t.e # t gefolgt von einem bel. Buchstaben # gefolgt von e # Dieses Muster ist enthalten in # the # tre # tle # aber nicht in te # oder tale ^f # f am Anfang einer Zeile ^ftp # ftp am Anfang einer Zeile e$ # e am Ende einer Zeile tle$ # tle am Ende einer Zeile und* # un gefolgt von 0 oder mehreren d # Dieses Muster ist enthalten in # un # und # undd # unddd (etc) .* # Irgendein String ohne newline, weil # . bedeutet irgendein Buchstabe ausser newline # und * bedeutet 0 oder mehrere davon ^$ # Leerzeile Aber das ist noch längst nicht alles. Eckige Klammern werden verwendet um irgendein Zeichen innerhalb zu erkennen. Wird innerhalb von eckigen Klammern ein Bindestrich - verwendet, bedeutet das einen Zeichenbereich, ein ^ am Anfang bedeutet 'keines von diesen': [qjk] # Entweder q oder j oder k [^qjk] # Weder q noch j noch k [a-z] # Irgendetwas zwischen a und z (inklusive) [^a-z] # Keine Kleinbuchstaben [a-zA-Z] # Irgendein Buchstabe [a-z]+ # Irgendeine Folge von Kleinbuchstaben Hier können wir vielleicht vorläufig aufhören und zur Uebungsaufgabe übergehen. Der Rest ist hauptsächlich als Referenz gedacht. Ein senkrechter Strich | bedeutet ein "OR" und Klammern (...) werden verwendet, um Dinge zu gruppieren: jelly|cream # Entweder jelly oder cream (eg|le)gs # Entweder eggs oder legs (da)+ # Entweder da oder dada oder dadada oder... Und noch ein paar Spezialzeichen mehr: \n # Zeilenumbruch \t # Tabulator \w # Irgendein alphanumerischer (word) Buchstaben # ist identisch mit [a-zA-Z0-9_] \W # nicht alphanumerisch (non-word) # ist identisch mit [^a-zA-Z0-9_] \d # Eine Zahl. Ist identisch mit [0-9] \D # Keine Zahl. Ist identisch mit [^0-9] \s # 'whitespace character': space, # tab, newline, etc \S # 'non-whitespace character' \b # Wortgrenze (nur ausserhalb []) \B # Innerhalb eines Wortes Zeichen, wie $, |, [, ), \, / sind Spezialfälle in RA's. Falls sie als normale Zeichen verwendet werden sollen, müssen sie mit einem 'backslash' \ markiert werden: \| # Vertical bar \[ # An open square bracket \) # A closing parenthesis \* # An asterisk \^ # A caret symbol \/ # A slash \\ # A backslash und so weiter. Ein paar BeispieleWie wir schon erwähnt haben, ist es vermutlich das Beste, mit einfachen Beispielen zu beginnen und langsam zu schwierigeren zu gehen. Im Perl-Programm müssen sie wie gesagt zwischen Schrägstrichen /.../ stehen. [01] # Enweder "0" or "1" \/0 # Eine Division durch Null: "/0" \/ 0 # Mit Leerzeichen: "/ 0" \/\s0 # Mit 'whitespace': # "/ 0" wobei das Leerzeichen auch ein Tab # etc. sein kann0 \/ *0 # Kein oder mehrere Leerzeichen # "/0" or "/ 0" or "/ 0" etc. \/\s*0 # Kein oder mehrere 'whitespace' \/\s*0\.0* # Wie vorher, aber mit Dezimalpunkt # und möglicherweise weiteren Nullen, zB. # "/0." und "/0.0" und "/0.00" etc und # "/ 0." und "/ 0.0" und "/ 0.00" etc. UebungenIm letzten Kapitel zählte unser Programm alle nicht-leeren Zeilen im File roman5.txt. Wir wollen es nun ändern, sodass es anstelle von den nicht-leeren Zeilen, nur Zeilen mit folgenden Eigenschaften zählt:
Das Programm soll weiterhin jede Zeile ausgeben, jedoch nur die obenerwähnten nummerieren. Wir wollen die $_-Variable verwenden um den Mustererkennungs-Operator =~ zu vermeiden.
Ersetzen, Uebersetzen in StringsSowie Perl Muster in Strings erkennen kann, können auch Muster durch Strings ersetzt werden. Dazu verwenden wird die s-Funktion, welche ähnlich wie im vi-Editor oder beim sed aussieht. Wiederum wird der Vergleichsoperator verwendet, welcher wiederum weggelassen werden kann, falls die Ersetzung auf der Defaultvariablen $_ gemacht werden soll. Um den Substring london durch London in der Variablen $satz zu ersetzen, verwenden wir die Anweisung $satz =~ s/london/London/; Mit der Defaultvariablen $_, sieht das folgendermassen aus s/london/London/; Beachte, dass die zwei regulären Ausdrücke (london und London) insgesamt von drei Schrägstrichen / umgeben sind. Das Resultat dieses Ausdruckes ist die Anzahl gemachter Ersetzungen. In diesem Fall Null (false) oder Eins (true). OptionenDieses Beispiel ersetzt nur das erste Auftreten von london im String. Verschiedene Optionen steuern das Verhalten der s-Funktion. Falls wir alle Vorkommnisse ersetzen wollen, müssen wir nach dem letzten Schrägstrich ein g anhängen. s/london/London/g; Wiederum ist das Resultat dieses Ausdruckes die Anzahl gemachter Ersetzungen. In diesem Fall Null (false) oder >Null (true). Falls wir auch Dinge, wie lOndon, lonDON, LoNDoN, ersetzen wollen, könnte das ganze folgendermassen aussehen: s/[Ll][Oo][Nn][Dd][Oo][Nn]/London/g Ein einfacherer Weg ist jedoch die Verwendung der i Option (i wie 'ignore case'). Der Ausdruck s/london/London/gi; macht eine globale Ersetzung ohne Beachtung der Gross- oder Kleinschreibung. Die i Option kann auch beim normalen Vergleich von Mustern /.../i verwendet werden. Muster wiederverwendenHäufig ist es sinnvoll, die aufgefundenen Muster wiederzuverwenden. Dabei werden die Muster in Klammern der Reihe nach in die Variablen $1,...,$9 zwischengespeichert. Diese Variablen können sowohl im gleichen regulären Ausdruck, als auch in der Ersetzung wiederverwendet werden, indem die speziellen Codes für reguläre Ausdrücke \1,...,\9 angewendet werden. Das Beispiel $_ = "Lord Whopper of Fibbing"; s/([A-Z])/:$1:/g; print "$_\n"; ersetzt jeden Grossbuchstaben durch denselben Buchstaben umgeben von zwei Doppelpunkten. Das heisst, die Ausgabe lautet: :L:ord :W:hopper of :F:ibbing. Die Variablen $1,...,$9 sind read-only Variabeln; sie können nicht direkt verändert werden. Im folgenden Beispiel wird die Testanweisung if (/(\b.+\b) \1/) { print "Found $1 repeated\n"; } jedes wiederholte Wort erkennen. Der Code \b stellt eine Wortbegrenzung dar und .+ erkennt jeden nicht-leeren String. Damit erkennt \b.+\b irgendein String zwischen Wortbegrenzern. Durch die Klammern wird eine Zwischenspeicherung veranlasst und diese wird innerhalb des regulären Ausdrucks mit \1, im restlichen Programm mit $1 referenziert. Der folgende Ausdruck vertauscht den ersten und den letzten Buchstaben einer Zeile in der $_-Variablen: s/^(.)(.*)(.)$/$3$2$1/ Der Code ^ erkennt den Anfang, $ das Ende einer Zeile. \1 speichert den ersten Buchstaben; \2 speichert alles bis zum letzten Buchstaben, welcher in \3 gespeichert wird. Danach wird die ganze Zeile ersetzt, indem $1 und $3 vertauscht werden. Nach einem Vergleich hat man drei Spezial-Variabeln $`, $& und $' zur Verfügung, in welchen die Teile vor, während und nach dem Muster abgespeichert sind. Nach $_ = "Lord Whopper of Fibbing"; /pp/; sind die folgenden Aussagen wahr: (Beachte, dass eq die Gleichheit von Strings bedeutet.) $` eq "Lord Who"; $& eq "pp"; $' eq "er of Fibbing"; Zum Schluss des Abschnittes über Muster-Wiederverwendung möchten wir noch darauf aufmerksam machen, dass Variabeln, welche innerhalb der Schrägstriche eines Vergleiches verwendet werden, interpoliert werden. Damit wird in $search = "the"; s/$search/xxx/g; jedes Vorkommen von the durch xxx ersetzt. Falls jedes Vorkommen von there ersetzt werden soll, kann nicht s/$searchre/xxx/ verwendet werden, da der Versuch unternommen wird, die Variable $searchre zu interpolieren. Falls hingegen der Variablenname in geschweifte Klammern gesetzt wird, sieht der richtige Code wie folgt aus: $search = "the"; s/${search}re/xxx/; UebersetzenDie Funktion tr ermöglicht die Uebersetzung von einzelnen Buchstaben. Die folgende Anweisung ersetzt in der Variablen $sentence jedes a mit einem e, jedes b mit einem d und jedes c mit einem f. Der Ausdruck gibt die Anzahl gemachter Ersetzungen zurück. $sentence =~ tr/abc/edf/; Die meisten der speziellen Codes für reguläre Ausdrücke können in der Funktion tr nicht verwendet werden. Die folgende Anweisung zum Beispiel, zählt die Anzahl Sterne in der Variablen $sentence und speichert sie in der $count-Variablen ab. $count = ($sentence =~ tr/*/*/); Der Bindestrich jedoch bedeutet immer noch einen Bereich ("zwischen"). Diese Anweisung übersetzt $_ in Grossbuchstaben: tr/a-z/A-Z/; UebungDas aktuelle Programm zählt die Zeilen, welche einen bestimmten String beinhalten. Aendere das Programm so, dass es die Zeilen zählt, welche einen Doppelbuchstaben enthalten. Diese Doppelbuchstaben sollen in Klammern ausgegeben werden. Zum Beispiel sollten folgende Zeilen erscheinen: «30?» Fabio Hi(pp)in steht auf dem Tre(pp)enabsatz und zieht kräftig an Sophies «Du, sag mal: Hast Du schon a(ll)es gezügelt?» Ein bisschen interessanter ist vielleicht eine Verallgemeinerung dieses Programmes. Wie möchten die Suchstrings als Argumente übergeben. Angenommen das Programm heisse countlines. Bei einem Aufruf countlines first second etc werden die Argumente im Array @ARGV abgespeichert. Somit ist $ARGV[0] gleich first, $ARGV[1] gleich second und $ARGV[2] gleich etc. Aendere das Programm, sodass es ein Argument akzeptiert und nur diejenigen Zeilen zählt, welche diesen String enthalten. Setze diese Vorkommnisse in Klammern. Damit wird countlines du unter anderem die folgende Zeile ausgeben: 022Blackcurrant-Büchse (du)rch die Luft, die er mit einem Hechtsprung auf den Mehr String-FunktionenSplitEine sehr nützliche Funktion in Perl ist die split-Funktion. Sie unterteilt einen String an definierten Stellen und kopiert die Teile in einen Array. Diese Funktion verwendet reguläre Ausdrücke für die Trennstellen und splittet die $_-Variable, falls nichts anderes spezifiziert wird. Sie funktioniert folgendermassen: $info = "Benno:Müller:Kaufmann:Hauptstrasse 14"; @personal = split(/:/, $info); Dies hat den gleichen Effekt, wie @personal = ("Benno", "Müller", "Kaufmann", "Hauptstrasse 14"); Falls die Information in der $_-Variablen gespeichert ist, können wir einfach schreiben: @personal = split(/:/); Falls die einzelnen Felder durch eine beliebige Anzahl von Doppelpunkten unterteilt sind, können wir einen regulären Ausdruck verwenden. Der Code $_ = "Hugo:Huber::dipl. math.:::Dammweg 2"; @personal = split(/:+/); bedeutet das gleiche wie @personal = ("Hugo", "Huber", "dipl. math.", "Dammweg 2"); Hingegegen würde $_ = "Hugo:Huber::dipl. math.:::Dammweg 2"; @personal = split(/:/); den folgenden Array ergeben: @personal = ("Hugo", "Huber", "", "dipl. math.", "", "", "Dammweg 2"); Ein Wort kann in Buchstaben, ein Satz in Wörter und ein Paragraph in Sätze aufgeteilt werden: @chars = split(//, $word); @words = split(/ /, $sentence); @sentences = split(/\./, $paragraph); Im ersten Fall wird der Null-String zwischen jedem Buchstaben erkannt, deshalb ist der @chars-Array ein Array von Buchstaben, dh. ein Array von Strings der Länge 1. substrEine weitere Funktion die auf Strings operiert ist die substr-Funktion. Hier sind drei Beispiele, wie sie verwendet wird: substr("Once upon a time", 3, 4); # returns "e up" substr("Once upon a time", 7); # returns "on a time" substr("Once upon a time", -6, 5); # returns "a tim" Das erste Beispiel ergibt einen Substring der Länge 4, welcher an der 3. Stelle
beginnt. Beachte, dass der erste Buchstaben eines Strings den Index 0 hat! Falls der negative Index vor den Anfang des Stringes zeigt, wird Perl nichts zurückgeben und eine Warnung ausgeben. Um das zu vermeiden, kann man einen String verlängern unter Verwendung des x-operators, den wir früher erwähnt haben. Zum Beispiel erzeugt der Ausdruck (" "x30) erzeugt 30 Leerzeichen. substr kann auch als lvalue verwendet werden (auf der linken Seite einer Anweisung): $str = "It's a Perl World."; substr($str, 7, 4) = "Small"; # It's a Small World substr($str, 13, 0) = "Perl "; # It's a Small Perl World UebungEin nützliches Werkzeug in der Verarbeitung von natürlichen Sprachen ist die Konkordanz. Sie erlaubt die Darstellung einer bestimmten Zeichenfolge in ihrem unmittelbaren Kontex. Zum Beispiel wird ein Konkordanzprogramm mit dem Suchstring 'the' angewendet auf ein bestimmtes File die folgende Ausgabe erzeugen. Beachte die Darstellung des Suchstringes in einer vertikalen Zeile. discovered (this is the truth) that when he t kinds of metal to the leg of a frog, an e rrent developed and the frog's leg kicked, longer attached to the frog, which was dea normous advances in the field of amphibian ch it hop back into the pond -- almost. Bu ond -- almost. But the greatest Electrical ectrical Pioneer of them all was Thomas Edi Wir wollen ein solches Programm als Uebungsbeispiel für unser File roman5.txt mit dem Suchstring du schreiben. Beachte die folgenden Hinweise:
Soweit so gut. Jedoch stehen die Suchstrings noch nicht untereinander in einer Zeile. Um das zu erreichen benötigen wir die substr-Funktion.
Binäre Daten und FilesAls C-Programmierer kennt man die Funktionen, um in binären Files zu manövrieren. In Perl ist es sehr ähnlich: Für Lesen gibt es read, für Schreiben print (nein, nicht write!), für Positionieren seek und um die aktuelle Position im File zu erhalten tell. Diese Funktionen gelten auch für Textfiles, werden jedoch meistens in Files mit fester Recordlänge (fixed record length) verwenden, wo beliebiger Zugriff auf einzelne Records (random access) durch einfache Berechnungen möglich ist. Zuerst machen wir als einfaches Beispiel die Kopie eines Files mit read und print: open FROM, "InFile"; open TO, ">OutFile"; while (read FROM, $buf, 16384) { print TO $buf; } close FROM; close TO open und close funktionieren wie früher besprochen. ( Es gäbe auch noch eine einfachere Möglichkeit, ein File zu kopieren: Mit dem Modul File::Copy. ) binäre DatenNehmen wir an, wir hätten ein File, welches von einem C-Program geschrieben wurde und zwar mit fester Recordlänge, dh. mit fwrite einen struct mit bekannter Definition. Wir wollen diese Records auslesen und den Inhalt der einzelnen Felder zugreifen. Dazu verwenden wir die Funktionen read und unpack. unpack benötigt als Parameter ein Template, welches die Struktur des Records (struct) beschreibt und einen String, welcher den Record beinhaltet. Das Template ist eine Folge von Buchstaben, welche die Reihenfolge und Art der einzelnen Felder des Records beschreibt:
Weitere Templates sind unter der Funktion pack zu finden. Ein Teil des C-Codes für obiges Beispiel sieht wie folgt aus: struct{char st[4]; int in; double d;} gaga; fwrite(&gaga, sizeof(gaga) 1 , fp) (* FILE *fp *) Perl-Code: $template = "a4 i d"; $len = length pack($template,'',0,0) read(FP,$rec,$len) # open FP, "filename"; ($str,$in,$d) = unpack($template,$rec); Mit pack kann ein Record wie in C erzeugt werden. Wir brauchen diese Funktion hier, um die Länge des Records zu bestimmen. Formattierte AusgabePerl liefert einen Mechanismus um einfache Reports und Tabellen auszugeben, der über die Möglichkeiten der print- und printf-Funktion hinausgeht. Man deklariert das Layout der Ausgabe mit format und gibt die einzelnen Records mit write aus. Die Deklaration format kann irgendwo im Programm erfolgen und hat die folgende Syntax: format NAME = FORMLIST . NAME ist der Formatname und wird defaultmässig dem gleichnamigen Filehandle
zugeordnet. Falls er weggelassen wird, wird Beispiel: Wir möchten nun das binäre File, welches wir oben gelesen habe, schön formattiert ausgeben: format TOP = Binaeres File STRING INTEGER DOUBLE ---------------------------------- . format = # String linksbuendig, Integer zentriert, Double rechtsbuendig @<<<< @|||||| @>>>>> $str,$in,$d . while (read(FP,$rec,$len)) { ($str,$in,$d) = unpack($template,$rec); write; } Für weitere Details zu Formaten, wende man sich an das Manual.
UebungSchreibe ein Programm, welches das File
SubroutinenWie jede gute Programmiersprache können in Perl eigene Funktionen programmiert werden. Man nennt sie in Perl Subroutinen (wie in Fortran!?). Sie können irgendwo im Programm platziert werden. Normalerweise wird man sie an den Anfang oder ans Ende des Programmes stellen. Wie in C kann eine Subroutine deklariert und später definiert werden unter Verwendung der Compilerdirektive use subs. sub NAME; # Deklaration sub NAME(PROTO); # Deklaration mit Prototypen sub NAME BLOCK # Definition sub NAME(PROTO) BLOCK # Definition mit Prototypen Beispiel: sub mysubroutine { print "Dies ist eine langweilige Subroutine.\n"; print "Sie macht immer das gleiche!\n"; } Alle folgenden Anweisungen rufen diese Subroutine auf. Beachte, dass der Buchstabe & am Anfang des Namens stehen muss. (V5: nicht mehr nötig, aber empfohlen.) &mysubroutine; # Aufruf ohne Parameter &mysubroutine($_); # Aufruf mit einem Parameter &mysubroutine(1+2, $_); # Aufruf mit zwei Parametern Im Allgemeinen gelten folgende Regeln für den Unterprogrammaufruf: NAME(LIST); # & ist fakultativ mit Klammern NAME LIST; # Klammern sind fakultativ falls vordefiniert oder importiert &NAME; # übergibt der Subroutine aktuellen @_ ParameterIm obigen Fall wurden die Parameter akzeptiert aber nicht verwendet. Mit dem Aufruf einer Subroutine werden alle übergebenen Parameter im Spezialarray @_ gespeichert. Diese Variable hat nichts zu tun mit der Spezialvariablen $_. @_ ist ein Array, $_ ist eine skalare Variable. Die folgende Subroutine gibt die Liste der Parameter aus, mit welcher sie aufgerufen wurde: sub printargs { print "@_\n"; } &printargs("perly", "king"); # Example prints "perly king" &printargs("frog", "and", "toad"); # Prints "frog and toad" Wie bei jedem anderen Array, können die einzelnen Elemente von @_ mit eckigen Klammern zugegriffen werden: sub printfirsttwo { print "Der erste Parameter war $_[0]\n"; print "und $_[1] war der zweite\n"; } Wir betonen noch einmal das $_[0] und $_[1] nichts zu tun haben mit der Spezialvariablen $_ und das beides gleichzeitig benützt werden darf, ohne Namenskollisionen befürchten zu müssen.
RückgabewerteDas Resultat einer Subroutine ist immer das Resultat des letzten evaluierten Ausdruckes. Diese Subroutine berechnet das Maximum von zwei gegebenen Eingabeparameter: sub maximum { if ($_[0] > $_[1]) { $_[0]; } else { $_[1]; } } Der Aufruf sieht so aus: $biggest = &maximum(37, 24); # $biggest ist 37 Die Subroutine printfirsttwo gibt ebenfalls einen Wert zurück, und zwar ist es 1. Die letzte Evaluation dieser Subroutine war die print-Anweisung. Das Resultat einer erfolgreichen print-Anweisung ist immer 1. Lokale Variablen mit myDie Variable @_ ist lokal zur aktuellen Subroutine. Um Namenskollisionen zu vermeiden, können auch andere Variablen lokal gemacht werden. Dazu verwenden wir die Funktionen my oder local. Häufig möchte man die Elemente von @_ einer eigenen lokalen Variablen zuordnen, etwa um die Lesbarkeit zu verbessern. Die folgende Subroutine testet, ob ein String Substring eines anderen ist, ohne die Leerzeichen zu berücksichtigen: sub inside { my ($a, $b); # Erzeuge lokale Var. ($a, $b) = ($_[0], $_[1]); # Param. zuordnen $a =~ s/ //g; # Leerzeichen aus lok. $b =~ s/ //g; # Var. löschen ($a =~ /$b/ || $b =~ /$a/); # Ist $b in $a # oder $a in $b? } &inside("lemon", "dole money"); # true Falls mehr als eine Variable mit my deklariert werden soll, müssen sie in Klammern angegeben werden (my ist eine Funktion!). Wir können das Ganze auch noch ein wenig eleganter schreiben, indem wir die ersten beiden Zeilen zusammenfassen: my ($a, $b) = @_; Lokale Variablen mit locallocal war in Perl 4 die einzige Möglichkeit lokale Variabeln zu deklarieren. Aus Kompatibilitätsgründen und für Spezialfälle ist es noch verfügbar. my ist die neuere (abPerl 5) und effizientere Art lokale Variablen zu
deklarieren. Der Unterschied zu local liegt in wichtigen Feinheiten. local
erzeugt einen temporären Wert für eine globale Variable (dynamic scoping), wohingegen my
eine echte lokale Variable deklariert (lexical scoping), welche nur im umgebenden Block,
Subroutine (oder eval) bekannt ist. Die Syntax ist bei beiden gleich. UebungMache aus der FOR-Schleife der letzten Uebung eine Subroutine mit zwei Parametern. Der erste ist der Suchstring, der zweite der Array aus den Teilstücken des Textes. Innerhalb der Subroutine sollen alle Variablen lokal sein (mit my). Rufe diese Subroutine im Hauptprogramm auf.
'Type Globs' und Referenzen'Type Globs'Typeglobs sind Einträge in die Symboltabelle. (Diese ist im Hash %:: abgespeichert.) In Perl kann man alle Objekte mit einem bestimmten Namen referenzieren, indem man diesen Namen mit dem Präfix * versieht: *foo. Das nennt man dann type globbing, weil der Stern als Wildcard für alle anderen Präfix-Buchstaben wie $, %, & stehen kann. Folgendes Beispiel soll diesen Sachverhalt erhellen: $foo = 100; @foo = ('aa','bb','cc'); sub foo { print "I am foo\n"; } sub deref { local(*bar) = @_; print $bar[0],"\n"; print $bar,"\n"; &bar; } print "Ref: ",*foo,"\n"; &deref(*foo); Die Ausgabe dieses Programmes sieht wie folgt aus: Ref: *main'foo aa 100 I am foo In Perl 4 wurde dieser Mechanismus verwendet, um die Uebergabe von Referenzen an Subroutinen zu simulieren. In Perl 5 werden dafür echte Referenzen verwendet (siehe unten). Natürlich funktioniert der alte Mechanismus auch in modernen Versionen. ReferenzenMir dem type globbing erhält man symbolische Referenzen, dh. sie beinhalten den
Namen einer Variablen, vergleichbar mit einem symbolischen Link in einem Unix-Filesystem.
In Perl 5 gibt es auch sogenannte harte Referenzen. Sie zeigen direkt auf das der
Variabeln zugrundeliegende Objekt, welches eine skalare Variable, ein Array oder ein
assoziativer Array (Hash) sein kann, aber auch eine Subroutine oder ein Filehandle. Diese
Referenzen sind 'intelligent'. Sie führen Buch über die Anzahl Referenzen auf ein Objekt
und geben das Objekt frei, sobald diese Anzahl auf Null geht. Erzeugen von Referenzen
$scalarref = \$foo; $arrayref = \@ARGV; $hashref = \%ENV; $coderef = \&handler; $globref = \*STDOUT; $arrayref = [1, 2, ['a', 'b', 'c']]; $hashref = {'Adam' => 'Eve', 'Clyde' => 'Bonnie'); $coderef = sub { print "Boink!" }; $objref = new Doggie (Tail => 'short', Ears => 'long'); $objref->bark(); # Aufruf einer Methode splutter(\*STDOUT); # Aufruf sub splutter { # Deklaration my $fh = shift; print $fh "gugus\n"; } Dereferenzieren
$bar = $$scalarref; push(@$arrayref, $filename); $$arrayref[0] = "Januar"; $$hashref{"KEY"} = "VALUE"; &$coderef(1,2,3); print $globref "output\n"; $bar = ${$scalarref}; push(@{$arrayref}, $filename); ${$arrayref}[0] = "Januar"; ${$hashref}{"KEY"} = "VALUE"; &{$coderef}(1,2,3); In diesen Fällen ist es natürlich überflüssig die geschweiften Klammern zu verwenden. Aber da ein Block einen beliebigen Ausdruck beinhalten kann, gibt es vernünftigere Beispiele: &{ $dispatch{$index} }(1,2,3); # Aufruf der korrekten Subroutine $arrayref->[0] = "Januar"; $hashref->{"KEY"} = "VALUE"; Die linke Seite vom Pfeil kann irgendein Ausdruck sein, welcher eine Referenz zurückgibt. $array[$x]->{"foo"}->[0] = "Januar"; Vor dieser Anweisung könnte $array[$x] undefiniert sein, wird aber an dieser Stelle automatisch zu einer Referenz auf einen Hash. Dasselbe gilt analog für $array[$x]->{"foo"}. Die Pfeile zwischen den Klammern müssen nicht geschrieben werden: $array[$x]{"foo"}[0] = "Januar"; $obj->methode($param); Wir werden im nächsten Kapitel mehr davon hören. Und dann gibt es noch die perlobj-Manpage.
Referenzen als Parameter von SubroutinenManchmal möchte man nicht einen Array als Wertparameter übergeben, sondern innerhalb
der Subroutine mit der globalen Variablen arbeiten (PASCAL: VAR-Parameter). Natürlich ist
es schneller eine Referenz zu übergeben, anstelle eines ganzen Arrays, daneben ist es die
einzige Möglichkeit, mehrere Arrays als Parameter zu übergeben. (Warum?) @letzte = popmany( \@a, \@b, \@c, \@d ); sub popmany { my $aref; my @retlist = (); foreach $aref (@_) { push @retlist, pop @$aref; } return @retlist; } Das ist ja alles sehr schön, aber wie bekomme ich mehrere Arrays oder Hashes als Rückgabewerte? Wie wär's mit folgendem: ($aref, $bref) = func(\@a, \@b); print "@$aref has more then @$bref\n"; sub func { my ($cref, $dref) = @_; if (@$cref > @$dref) { return ($cref, $dref); } else { return ($dref, $cref); } }
UebungMan nehme die Uebung des letzten Kapitels und tausche die Parameter der Subroutine aus. Damit das funktioniert, muss man eine Referenz auf den Array der Textstücke übergeben und in der Subroutine den Array dereferenzieren.
ModulePackagesPerl stellt ein Mechanismus zur Verfügung, der es erlaubt, verschiedenen Namensbereiche zu definieren, welche sich nicht überlappen. Dies ist die Grundlage für die Verwendung von Perl-Bibliotheken und für die Entwicklung grösserer Applikationen (Modularisierung). Ausser ein paar speziellen Variablen gibt es in Perl eigentlich keine globalen Variablen, da jeder Variablenname automatisch zum Package main gehört. Man kann mit der package-Anweisung den gewünschten Namensbereich auswählen. Der Gültigkeitsbereich einer package-Anweisung beginnt bei der Anweisung und endet am Ende des umgebenden Blockes. Variablen eines anderen Packages können mit folgender Notation referenziert werden: $Package::Variable $Package'Variable (Notation von Perl V4) Beispiele: { Die package-Anweisung wird häufig an den Anfang eines Files gesetzt, welches mit der require-Anweisung von einem anderen File verwendet wird. Konstruktoren und Destruktoren von PackagesZwei spezielle Subroutinen werden von Perl als Konstruktoren resp. Destruktoren eines
Package interpretiert. Sie heissen BEGIN resp. END. Perl-KlassenEs gibt keine spezielle Syntax für Klassen in Perl. Ein Package kann als Klasse gelten, falls sie Subroutinen zur Verfügung stellt, welche Methoden sind. Ein solches Package kann Methoden von anderen Klassen ableiten, indem sie die Namen dieser Klassen in dem @ISA-Array angibt. package subclass; @ISA = (baseclass); sub new { my $self = {}; bless $self; return $self; } Weitere Informationen findet man in der perlobj-Manpage. ModuleEin Modul ist ein Package, welches in einem File mit dem gleichen Namen als Bibliothek
abgespeichert ist und so gestaltet ist, dass man es wiederverwenden kann. Das heisst, es
kann einen Mechanismus zur Verfügung stellen, der es erlaubt, einige seiner Symbole
in das Package, welches es verwendet, zu exportieren. Will man zum Beispiel ein Modul mit dem Namen Gugus definieren, erzeugt man ein File mit dem Namen Gugus.pm und setzt folgende Zeilen an den Anfang dieses Files: package Gugus; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(func1 func2); @EXPORT_OK = qw($hugo @egon %mueller func3); Der Array @EXPORT beinhaltet die Symbole, welche per default exportiert werden, der Array @EXPORT_OK diejenigen, welche auf Anfrage exportiert werden können. Perl Module werden mit use aufgerufen: use Module; oder use Module LIST; aufgerufen. Die Liste LIST beinhaltet die gewünschten Symbole, welche in den aufrufenden Namensbereich importiert werden sollen. Dies ist äquivalent zu folgenden Anweisungen: BEGIN {require "Module.pm"; import Module; } resp. BEGIN (require "Module.pm"; import Module LIST; }
require "Cwd.pm"; # Cwd:: $here = Cwd::getcwd(); use Cwd; # Importiere Symbole von Cwd $here = getcwd(); use Cwd(); # Importiere leere Liste von Cwd $here = getcwd(); # Fehler: getcwd() nicht bekannt!! require "Cwd.pm"; # Cwd:: $here = getcwd(); # Fehler: kein main::getcwd()!! An dieser Stelle ist es vielleicht interessant zu wissen, wo und welche Module auf meinem System vorhanden sind. % perl -V # gibt viele Einzelheiten über die Installation an # inklusive @INC , den Modul-Suchpfad % perldoc perldoc # Modulbeschreibungen Uebung 1Wechsle in das Verzeichnis /tmp und lese die Fileliste in einen Array ein und wechsle in das Startverzeichnis zurück. Drucke den Namen des aktuellen Arbeitverzeichnisses vor und nach den Wechseln aus: Startverzeichnis # wechseln Tmp-Verzeichnis Fileliste # zurück wechseln Startverzeichnis Verwende dazu das Standard-Modul Cwd.pm. Beachte: Es gibt verschiedene Möglichkeiten, den Inhalt eines Verzeichnisses zu lesen:
open(FILES,"/bin/ls *|"); while ($File = <FILES>) {..} Uebung 2Mache aus der letzten Uebung ein Modul, welches die Subroutine enthält und verwende ein anderes File für das Hauptprogramm. Im Hauptprogramm wird mit use das Modul importiert. Teste die zwei syntaktischen Varianten use Module; use Module LIST;
Einführung in Perl 5 ObjekteViele Leute schrecken vor den praktischen Perl5-Modulen zurück, weil diese mit Objekten zu tun haben. Und das ist ja schon mal verdächtig und tönt nach etwelchen Schwierigkeiten ohne entsprechend nützlich zu sein. Auf der anderen Seite führen gewisse Probleme automatisch hin zu Objekten. Das macht den Leuten Angst. Unnötigerweise. Es ist ein riesiger Unterschied genügend über OO-Programmierung zu wissen, um Module zu verwenden , oder um Module selber entwerfen und implementieren zu können. Man befürchtet das letztere können zu müssen um Zugang zum ersteren zu erhalten. Das ist weit weg von der Realität. Man muss nicht viel wissen um Module verwenden zu können. Man lädt einfach die Library und ruft die dokumentierten "Konstruktoren" auf. "Konstruktoren" sind die Dinge, welche neue Objekte erzeugen. Häufig werden sie new genannt. In Perl5 werden ein oder zwei neue Konzepte verwendet, welche Sie vielleicht bisher noch nie gesehen haben:
$obj = new CGI; $obj = CGI->new(); use LWP::UserAgent; $ua = new LWP::UserAgent; $ua->agent("Mozilla/5.0PlatinumB3"); # hee :-) $req = new HTTP::Request GET => 'http://perl.com/perl/'; $req->header('Accept' => 'text/html'); # send request $result = $ua->request($req); # check the outcome if ($result->is_success) { print $result->content; } else { print "Error: " . $result->code . " " . $result->message; } Das wär's. Das ist alles. Mehr braucht man nicht um Module in Perl zu verwenden. Hier sind noch ein paar Beispiele für den Gebrauch von perl WWW-Modulen: use CGI; $req = CGI->new(); use CGI::Imagemap; $map = CGI::Imagemap->new(); use HTML::Parse; $html = parse_htmlfile("test.html"); use URI::URL; $url = URI::URL->new('gisle.gif','http://www.com/%7Euser'); use HTTP::Request; $hreq = HTTP::Request->new('GET', 'http://www.perl.com/'); Ich glaube nicht, dass man ein Smalltalk- oder C++-Superguru sein muss, um mit diesen Dingen umzugehen. Aber falls Sie wirklich mehr darüber lernen möchten, können Sie die perlobj manpage konsultieren. |
last update: 14.9.1998, Roland Dietlicher , Systemdienste/Informatikdienste ETH Zürich |