KamelPerl 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:

  1. Das erste Perl Programm
    • Die erste Zeile
    • Kommentare und Anweisungen
    • Einfache Ausgabe
  2. Ein Programm ausführen
  3. Skalare Variablen
    • Operationen und Zuweisungen
    • Interpolation
    • Uebung
  4. Array Variablen
    • Array Zuweisungen
    • Arrays ausgeben
    • Uebung
  5. Assoziative Arrays
    • Operatoren
    • Umgebungs-Variablen
  6. Files
    • Uebung
  7. Kontrollstrukturen
    • foreach
    • Bool'sche Operationen
    • for
    • while und until
    • Uebung
  8. Bedingungen
    • Uebung
  9. Mustererkennung
    • Reguläre Ausdrücke (RA)
    • Die Defaultvariable $_
    • Mehr über RA's
    • Ein paar Beispiele
    • Uebung
  10. Ersetzen, Uebersetzen von Strings
    • Optionen
    • Muster wiederverwenden
    • Uebersetzen
    • Uebung
  11. Mehr String-Funktionen
    • Split
    • Substr
    • Uebung
  12. Binäre Daten und Files
    • Binäre Daten
    • Formattierte Ausgabe
    • Uebung
  13. Subroutinen
    • Parameter
    • Rückgabewerte
    • Lokale Variablen mit my
    • Lokale Variablen mit local
    • Uebung
  14. 'Typeglobs' und Referenzen
    • Typeglobs
    • Referenzen
    • Uebung
  15. Module
    • Packages
    • Klassen
    • Module
    • Uebung 1
    • Uebung 2
  16. Einführung in Perl 5 Objekte

 


Kamel 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.

 


Kamel 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.

 


Kamel 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.


Operationen und Zuweisungen

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.

 


KamelArray 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!

 


Kamel 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";

Kamel 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>).


Uebung

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.

 


Kamel 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.


Bool'sche Operatoren

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?

for

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!


while und until

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

Uebung

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...

 


Kamel 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.


Uebung

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.

 


Kamel 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.


Mehr über RAs

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.


Ein paar Beispiele

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.

Uebungen

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.

 


KamelErsetzen, 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

Kamel 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.

 


Kamel 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
  • f Float
  • d Double
  • 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).

 


KamelSubroutinen


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.

 


Kamel'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

  1. Mit dem Backslash-Operator:
  2. $scalarref = \$foo;
    $arrayref  = \@ARGV;
    $hashref   = \%ENV;
    $coderef   = \&handler;
    $globref   = \*STDOUT;
  3. Als Referenz zu einem anonymen Array, Hash oder Funktion:
  4. $arrayref = [1, 2, ['a', 'b', 'c']];
    
    $hashref = {'Adam' => 'Eve', 'Clyde' => 'Bonnie');
    
    $coderef = sub { print "Boink!" };
  5. Referenzen werden häufig durch spezielle Subroutinen, den Konstruktoren zurückgegeben:
  6. $objref = new Doggie (Tail => 'short', Ears => 'long');
    $objref->bark();     # Aufruf einer Methode
  7. Referenzen zu Filehandles werden durch Referenzen zu einem Typeglob erzeugt. Folgendes Beispiel zeigt, wie ein Filehandle als Parameter einer Subroutine übergeben wird:
  8. splutter(\*STDOUT);  #  Aufruf
    sub splutter {       #  Deklaration
        my $fh = shift;
        print $fh "gugus\n";
    }

Dereferenzieren

  1. 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.
  2. $bar = $$scalarref;
    push(@$arrayref, $filename);
    $$arrayref[0] = "Januar";
    $$hashref{"KEY"} = "VALUE";
    &$coderef(1,2,3);
    print $globref "output\n";
  3. 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.
  4. $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
  5. Als syntaktische Variante geht auch:
  6. $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";
  7. 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:
  8. $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.

 


KamelModule


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;

 


Kamel 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.

last update: 14.9.1998, Roland Dietlicher , Systemdienste/Informatikdienste ETH Zürich