Perl Tutorial
Dies 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 Perl Programm
- Die erste Zeile
- Kommentare und Anweisungen
- Einfache Ausgabe
- Ein Programm ausführen
- Skalare Variablen
- Operationen und Zuweisungen
- Interpolation
- Uebung
- Array Variablen
- Array Zuweisungen
- Arrays ausgeben
- Uebung
- Assoziative Arrays
- Operatoren
- Umgebungs-Variablen
- Files
- Kontrollstrukturen
- foreach
- Bool'sche Operationen
- for
- while und until
- Uebung
- Bedingungen
- Mustererkennung
- Reguläre Ausdrücke (RA)
- Die Defaultvariable $_
- Mehr über RA's
- Ein paar Beispiele
- Uebung
- Ersetzen, Uebersetzen von Strings
- Optionen
- Muster wiederverwenden
- Uebersetzen
- Uebung
- Mehr String-Funktionen
- Binäre Daten und Files
- Binäre Daten
- Formattierte Ausgabe
- Uebung
- Subroutinen
- Parameter
- Rückgabewerte
- Lokale Variablen mit my
- Lokale Variablen mit local
- Uebung
- 'Typeglobs' und Referenzen
- Typeglobs
- Referenzen
- Uebung
- Module
- Packages
- Klassen
- Module
- Uebung 1
- Uebung 2
- Einführung in Perl 5 Objekte
Das erste
Programm
Hier 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 Zeile
Jedes 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 Anweisungen
Das # 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 Ausgabe
Die 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ühren
Schreibe 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 Variablen
Die 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.
Perl 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.
Substitution
Folgende 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.
Uebung
Wir 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
Variablen
Eine 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 Zuweisung
Wie 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 ausgeben
Weil 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.)
Uebung
Wir 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
Operatoren
Die 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.
In skalarem Kontext geben die Funktionen keys und values die Anzahl
key/value-Paare im assoziativen Array an.
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-Variablen
UNIX 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";
Files
Das 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>).
Wir ä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.
Kontrollstrukturen
Weitere 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.
foreach
Um 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.
Die 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?
Die 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!
Das 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
Wir ä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...
Bedingungen
Natü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.
Wir 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.
Mustererkennung
Eine 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ücke
Ein 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.
RA 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?
Falls die obige Zeichenfolge am Anfang des Strings vorkommen soll, schreibt es sich so: /^a[ab]*c/,
oder am Ende: /a[ab]*c$/.
Anstelle unserer Zeichenfolge möchten wir Zahlen haben. Für Zahlen gibt es ein
Spezialzeichen \d, was nichts anderes bedeutet als [0123456789]
oder in Kurzform [0-9]. Diese drei Darstellungen sind äquivalent. Nehmen
wir an, wir hätten eine Nummer am Anfang unseres Strings und anschliessend ein
Leerzeichen. Der RA heisst /^\d+ /. Es könnte aber auch sein, dass
anstelle eines Leerzeichens auch ein Tabulator oder ein Newline steht, dazu gibt es
wiederum ein Spezialzeichen, das \s. Also heisst der RA /^\d+\s/.
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.
Wie 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.
Im 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:
- dem Buchstaben q
- dem String Du
- dem String Du, welcher ein grosses oder ein kleines D haben kann
- das Wort Du mit oder ohne Grossbuchstaben. Verwende \b um
Wortgrenzen zu erkennen.
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 Strings
Sowie 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).
Optionen
Dieses 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 wiederverwenden
Hä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/;
Uebersetzen
Die 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/;
Uebung
Das 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-Funktionen
Split
Eine 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.
substr
Eine 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!
Beim zweiten Beispiel wird der letzte Parameter, die Länge des Substrings, weggelassen.
Das Resultat ist ein Substring, welcher von der angegebenen Stelle bis zum Ende des
Strings reicht.
Das dritte Beispiel benützt einen negativen Index. Es ergibt einen Substring, der an der
6. Stelle vom Ende des Strings an 5 Buchstaben lang ist.
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
Uebung
Ein 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:
- Das ganze File soll in einen Array eingelesen werden (dies ist natürlich im Allgemeinen
nicht angebracht, da das File sehr gross sein kann, aber wir wollen uns jetzt nicht darum
kümmern.) Jede Zeile des Files ist ein Element des Arrays.
- Die chop -Funktion mit einem Array als Parameter schneidet den letzten Buchstaben jedes
Array-Elementes ab.
- Wie wir schon gesehen haben, kann der gesamte Array mit folgender Anweisung
zusammengefügt werden: $text = "@lines";
- Verwende die split-Funktion mit dem Suchstring als regulärer Ausdruck für die
Trennstelle, an welcher der Text unterteilt werden soll. Das Resultat ist ein Array aller
Substrings, welche vom Suchstring eingeschlossen sind.
- Drucke der Reihe nach jedes Arrayelement, den Suchstring und das nächste Arrayelement
aus
- Beachte, dass das letzte Element des Arrays @food den Index $#food hat.
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 Files
Als 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 Daten
Nehmen 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:
- A,a ASCII String
- b,B Bit String
- h,H Hex String
- c,C Signed/Unsigned Char
- s,S Signed/Unsigned Short
- i,I Signed/Unsigned Integer
- l,L Signed/Unsigned Long
- p Pointer to a null-terminated string.
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 Ausgabe
Perl 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 STDOUT angenommen.
FORMLIST besteht aus einer Folge von Zeilen, die jede entweder eine
Kommentarzeile sein kann, mit einen #-Zeichen am Anfang, eine Format-Zeile, welche das
Aussehen der Ausgabe beschreibt, oder eine Parameterzeile, welche die auszugebenden
Variablen zu der vorangehenden Format-Zeile angibt. Mit der gleichen Syntax kann das
Format für eine oder mehrere Kopfzeilen angegeben werden. Der Name von
Kopfzeilen-Formaten hat die Form FILEHANDLE_TOP , resp. für STDOUT
nur TOP . Es gibt wiederum eine Anzahl von Spezialvariablen, welche im
Zusammenhang mit Formaten gebraucht werden können. Zum Beispiel ist $~
gleich dem Namen des aktuellen Ausgabeformates, resp. $^ derjenige, des
aktuellen Kopfzeilen-Formates.
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.
Uebung
Schreibe ein Programm, welches das File /var/adm/wtmp eines UNIX-Systemes
ausliest. Dieses File enthält die Informationen über die Logins des Systems.
Formattiere die Ausgabe mit format . Die Struktur des Files
/var/adm/wtmp ist systemabhängig. (Siehe man utmp ).
Subroutinen
Wie 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 @_
Parameter
Im 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ückgabewerte
Das 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 my
Die 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 local
local 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.
Eine Einschränkung gibt es bei den Variablennamen: Mit my sind nur alphanumerische
Zeichen erlaubt! Um Spezialvariablen, wie zB. $\ zu lokalisieren, muss local
verwendet werden.
Uebung
Mache 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.
Referenzen
Mir 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.
Eine Variable wird von Perl nie implizit dereferenziert. Falls eine skalare Variable eine
Referenz ist, wird sie sich immer als skalare Variable verhalten und nicht als der Typ,
den sie referenziert. Diese Tatsache hat syntaktische Konsequenzen (siehe perldsc- , resp.
perlLoL- Manpage).
Erzeugen von Referenzen
- Mit dem Backslash-Operator:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*STDOUT;
- Als Referenz zu einem anonymen Array, Hash oder Funktion:
$arrayref = [1, 2, ['a', 'b', 'c']];
$hashref = {'Adam' => 'Eve', 'Clyde' => 'Bonnie');
$coderef = sub { print "Boink!" };
- Referenzen werden häufig durch spezielle Subroutinen, den Konstruktoren zurückgegeben:
$objref = new Doggie (Tail => 'short', Ears => 'long');
$objref->bark(); # Aufruf einer Methode
- Referenzen zu Filehandles werden durch Referenzen zu einem Typeglob erzeugt.
Folgendes Beispiel zeigt, wie ein Filehandle als Parameter einer Subroutine übergeben
wird:
splutter(\*STDOUT); # Aufruf
sub splutter { # Deklaration
my $fh = shift;
print $fh "gugus\n";
}
Dereferenzieren
- Ueberall, wo man einen Identifier als Teil eines Variablen- oder Subroutinennamens
schreiben würde, kann dieser Identifier durch eine einfache skalare Variable, welche eine
Referenz auf den gewünschten Typ darstellt, ersetzt werden.
$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = "Januar";
$$hashref{"KEY"} = "VALUE";
&$coderef(1,2,3);
print $globref "output\n";
- Ueberall, wo man einen Identifier als Teil eines Variablen- oder Subroutinennamens
schreiben würde, kann dieser Identifier durch einen BLOCK, welcher eine Referenz auf den
gewünschten Typ zurückgibt, ersetzt werden.
$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
- Als syntaktische Variante geht auch:
$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";
- Eine Referenz kann aber auch eine Referenz auf ein Objekt (zu einer Klasse) sein. Dann
gibt es vermutlich Methoden, welche durch diese Referenz zugegriffen werden können:
$obj->methode($param);
Wir werden im nächsten Kapitel mehr davon hören. Und dann gibt es noch die
perlobj-Manpage.
Mit den Referenzen von Perl 5 ist es einfach mehrdimensionale Datenstrukturen (Arrays von
Arrays, Arrays von Hashes, Hashes von Subroutinen etc. zu erzeugen (siehe perldsc- resp.
perlLoL-Manpage).
Weitere Angaben über Referenzen findet man in den perlref -Manpage.
Referenzen als Parameter von Subroutinen
Manchmal 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?)
Das folgende Beispiel gibt die letzten Elemente von einer Liste von Arrays aus:
@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);
}
}
Uebung
Man 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.
Module
Packages
Perl 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:
{
package MyPack;
$foo = 100;
$bar = 'aaa';
$::spe = 'hallo'; # oder $main::spe
}
$foo = 200; $bar = 'bbb';
print $foo, $bar; # 200bbb
print $MyPack::foo, $MyPack::bar; # 100aaa
print $spe; # hallo
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 Packages
Zwei spezielle Subroutinen werden von Perl als Konstruktoren resp. Destruktoren eines
Package interpretiert. Sie heissen BEGIN resp. END.
Sobald BEGIN vollständig definiert ist, wird sie auch ausgeführt, das heisst,
bevor der Rest des Files vollständig geparst ist. Damit kann ein BEGIN-Block
Definitionen von Subroutinen und ähnlichem von anderen Files importieren und damit dem
Parser sichtbar machen.
END wird ganz am Ende, beim Beenden des Interpreters ausgeführt.
Perl-Klassen
Es 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.
Module
Ein 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.
Es kann aber auch als Klassendefinition aufgefasst werden, die seine Funktionsweise via
Methoden zur Verfügung stellt, ohne Symbole explizit zu exportieren.
Oder es kann ein bisschen von beidem sein.
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; }
Alle Perl-Module sollten die Endung '.pm' haben. use nimmt an, dass diese Endung vorhanden
ist und ergänzt den Filenamen entsprechend. Schauen wir uns noch ein letztes Beispiel an,
welches den Unterschied zwischen use und require aufzeigt:
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 1
Wechsle 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:
- readdir
- `ls` resp qx{ ls }
- Pipe von /bin/ls:
open(FILES,"/bin/ls *|");
while ($File = <FILES>) {..}
Uebung 2
Mache 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
Objekte
Viele 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:
- Die Anweisung use Module lädt ein Modul mit dem Namen Module.pm (zur
Kompilationszeit) und fügt die exportierten Namen in Ihren Namensraum ein. Die Anweisung require
Module lädt das Modul ebenfalls, aber erst zur Laufzeit und importiert keine Namen.
- Der Pfeil-Operator für die Dereferenzierung -> bedeutet ein Aufruf einer
Methode (Subroutine) eines Objektes. In OO-Slang sagt man auch 'dem Objekt eine Message
senden'. Dabei braucht man keinerlei Kenntnisse über den Aufbau des Objektes. Es ist
einfach eine Black Box, welche Subroutinen zur Verfügung stellt. Das ist alles.
- Die folgenden zwei Zeilen sind aequivalent. Die zweite Zeile ist jedoch syntaktisch ein
wenig klarer:
$obj = new CGI;
$obj = CGI->new();
Wie jeder andere Unterprogrammaufruf, kann eine Methode irgend etwas zurückgeben, sogar
ein anderes Objekt.
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.
|