CLUE Computerlinguistik Uni Erlangen
Vorher Zurück Weiter
Weiter: Siebte Sitzung Zurück: Unterlagen Vorher: Sechste Sitzung (Referat)

Sechste Sitzung - Einführung in Perl

Grundlegende Konzepte von Perl

Perl
Practical Extraction and Report Language
Praktische Extraktions- und Berichtssprache
Perl
Pathologically Eclectic Rubbish Lister
Krankhaft zusammengewürfelter Blödsinnsauflister
Ein Shell-Skript ist nicht mehr als eine Reihe von Shell-Befehlen in einer Textdatei, die dann noch ausführbar (eXecutable) gemacht werden muß. Dann kann man den Dateinamen in der Shell eingeben und hat ein Shell-Skript.

Als Beispiel soll hier ein Skript dienen, welches erst Datum und Uhrzeit ausgibt (date) und dann anzeigt wer auf dem System eingeloggt ist (who).

$ echo date > IrgendeinSkript
$ echo who >> IrgendeinSkript 
$ cat IrgendeinSkript 
date
who
$ chmod +x IrgendeinSkript
$ ./IrgendeinSkript 
Thu Jun 18 16:21:54 METDST 1998
joerg      console      Jun 18 15:46
joerg      pts/0        Jun 18 15:48
joerg      pts/1        Jun 18 16:16
$
    
Ähnlich besteht ein Perl-Skript aus einer Reihe von Perl-Befehlen in einer Datei die ausführbar ist. Damit das System weiß, daß es sich hier um ein Perl-Skript und nicht um ein Shell-Skript handelt, muß in einem Perl-Skript als erste Zeile
#!/opt/perl5/bin/perl
    
stehen. Der Pfad hängt natürlich davon ab, wo auf dem System Perl installiert ist.

Zwischen den Elementen von Perl können beliebige und beliebig viele (auch keine) Whitespace-Zeichen wie Leerzeichen (spaces), Tabulatoren (tabs), Zeilenendezeichen (newline) etc. stehen, es sei denn es kommt zu Mehrdeutigkeiten.

Kommentare werden mit dem Nummernzeichen (#) markiert und gehen dann bis zum Zeilenende.

Der Perl-Interpreter parst und kompiliert das Skript komplett, bevor es ausgeführt wird. Das heißt, daß es keine Syntaxfehler mehr gibt, wenn das Skript einmal läuft, sowie daß Whitespace-Zeichen und Kommentare einfach entfernt wurden und das Skript nicht verlangsamen.

Ein Bummel durch Perl

Mit dem Basteln einer kleinen Anwendung, wollen wir eine Anzahl von verschiedenen Perl-Eigenschaften vorstellen, aber nur kurz erklären. Die ausführliche Erklärung kommt später, wenn wir diesen Punkt explizit abhandeln.

Das obligatorische ,,Hallo, Welt`` - Skript

Hier also ein Skript das etwas tut. Es schreibt
#!/opt/perl5/bin/perl
print "Hallo, Welt!\n";
    
Die erste Zeile gibt dem System an, daß es sich um ein Perl-Skript handelt, für Perl ist es ein Kommentar.

Die zweite Zeile ist der ausführbare Teil des Skripts, nämlich ein print-Befehl. Er fängt an mit dem Schlüsselwort print und hat als Argument eine Zeichenkette (string) die durch Anführungszeichen begrenzt (gequotet) wird. Darin enthalten ist die Zeichenkombination \n, welche für das Zeilenendezeichen (newline) steht. Abgeschlossen wird jeder Befehl in Perl mit einem Semikolon (;).

Fragen stellen und sich an die Eingabe erinnern

Nun soll das ,,Hallo, Welt`` - Skript uns mit Namen begrüßen. Dazu brauchen wir eine Möglichkeit den Namen zu speichern, eine Methode ihn Abzufragen und eine, um eine Antwort zu erhalten.

Um Daten (wie den Namen) zu speichern, gibt es beispielsweise skalare Variablen. Diese bestehen aus dem Dollar-Zeichen ($) gefolgt von dem Namen der Variablen, hier $Name.

Dann brauchen wir die Möglichkeit, eine Frage zu stellen und die Antwort zu erhalten. Um die Frage zu stellen, können wir wieder die print-Anweisung benutzen; um die Antwort zu erhalten verwenden wir den <STDIN>-Operator, welcher so wie wir ihn hier verwenden eine Zeile einliest. Diese Eingabe weisen wir dann der Variablen $Name zu.

print "Wie lautet Dein Name? ";
$Name = <STDIN>;
    
Der Wert von Name enthält zu diesem Zeitpunkt am Ende noch das Zeilenendezeichen, also hier Jörg\n. Um dies loszuwerden gibt es die chomp()-Anweisung, welcher von dem String-Wert einer skalaren Variablen das letzte Zeichen entfernt, sofern es ein Zeilenendezeichen ist.
chomp ($Name);
    
Nun müssen wir nur noch Hallo, gefolgt von dem Wert der Variablen $Name ausgeben, was wieder mit der print-Anweisung und einem gequoteten String geschieht, in den die Variable eingebettet wird.
print "Hallo, $Name!\n";
    
Wenn man wirklich ein Dollarzeichen und keine skalare Variable in der Ausgabe haben will, muß man vor das Dollarzeichen einen Rückstrich (backslash - \) stellen (escapen). Alles zusammen ergibt sich das folgende Skript:
#!/opt/perl5/bin/perl
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
print "Hallo, $Name!\n";
    

Hinzufügen von Wahlmöglichkeiten

Nun wollen wir eine besondere Begrüßung für Jörg und Oliver, aber eine Standardbegrüßung für alle anderen. Dazu brauchen wir eine Möglichkeit die Eingabe mit den Strings Joerg und Oliver zu vergleichen, um dann die besondere Begrüßung auszulösen. Dies geschieht auch in Perl mit einem if-then-else-Zweig und einem Vergleich.
#!/opt/perl5/bin/perl
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name eq "Joerg") ||
    ($Name eq "Oliver")) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
    }
    
Der eq-Operator vergleicht zwei Strings Zeichen für Zeichen und liefert dann den Wahrheitswert wahr, wenn sie gleich sind und die gleiche Länge haben.

Der if-Befehl wählt dann aus welcher Block von Befehlen (welche zwischen geschweiften Klammern stehen) ausgeführt wird. Der erste, wenn der Ausdruck wahr ist, ansonsten der zweite.

Raten eines geheimen Worts

Nun sollen alle außer Jörg und Oliver nach dem geheimen Wort gefragt werden. Das Skript soll bei falscher Eingabe solange wiederholt nach dem geheimen Wort fragen, bis richtig geraten wurde.
#!/opt/perl5/bin/perl
$GeheimesWort = "mrd";
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name eq "Joerg") ||
    ($Name eq "Oliver")) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while ($Versuch ne $GeheimesWort) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }
    
Als erstes wird das geheime Wort definiert, indem es der skalaren Variablen $GeheimesWort zugewiesen wird. Dann wird nach der Begrüßung mit einem weiteren print nach dem geheimen Wort gefragt. Der Rateversuch wird mittels des ne-Operators mit dem geheimen Wort verglichen (dem Gegenteil des eq-Operators). Das Ergebnis dieses Vergleichs kontrolliert eine while-Schleife, welche den Block solange ausführt, bis der Vergleich wahr ist.

Mehrere geheime Wörter

Nun soll dies Skript so modifiziert werden, daß mehr als nur ein geheimes Wort gültig ist. Dazu speichern wir alle möglichen Antworten in einer Datenstruktur, die Liste (Array) genannt wird. Jedes Element eines Array ist dabei eine einzelne skalare Variable die unabhängig verarbeitet werden kann. Aber auch der ganze Array kann auf einen Schlag einen Wert erhalten:
@Woerter = ("mrd","woerterbuch","sgml","tei","celex","malaga","lexikon");
    
Die Namen von Array-Variablen beginnen mit dem Zeichen @, so daß sie sich von den Namen der skalaren Variablen unterscheiden. Wenn der Array eingerichtet ist, können wir auf jedes seiner Elemente zugreifen, indem wir einen Index verwenden. Dann ist $Woerter[0] gleich mrd, $Woerter[1] gleich woerterbuch, $Woerter[2] gleich sgml, $Woerter[3] gleich tei, $Woerter[4] gleich celex, $Woerter[5] gleich malaga und $Woerter[6] gleich lexikon. Der Index kann genausogut ein Ausdruck sein, so daß wenn wir $i auf 2 setzten, $Woerter[$i] gleich sgml ist. Die Indexreferenzen fangen mit einem $ statt mit einem @ an, weil sie sich auf ein skalares Element eines Array und nicht auf den ganzen Array beziehen.
#!/opt/perl5/bin/perl
@Woerter = ("mrd","woerterbuch","sgml","tei","celex","malaga","lexikon");
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name eq "Joerg") ||
    ($Name eq "Oliver")) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      $i = 0;
      $Richtig = "vielleicht";
      while ($Richtig eq "vielleicht") {
        if ($Woerter[$i] eq $Versuch) {
          $Richtig = "ja";
        } elsif ($i < 6) {
          $i = $i + 1;
        } else {
          print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
          $Versuch = <STDIN>;
          chomp ($Versuch);
          $i = 0;
        }
      }
    }
    
Hier wird jetzt die skalare Variable $Richtig verwendet, um die while-Schleife zu steuern. Mit $i wird das Array @Woerter abgearbeitet.

Jeder bekommt sein eigenes geheimes Wort

Dazu erstellen wir erst einmal eine Tabelle mit Personen und dazugehörenden geheimen Wörtern.

Person Geheimes Wort
Jörg mrd
Oliver mrd
Besim woerterbuch
Tobias sgml
Dorota tei
Cecilia celex
Oliver S. malaga
Matthias lexikon

Um eine solche Tabelle in Perl zu speichern eignet sich am besten ein assoziativer Array (hash). Jedes Element des Arrays enthält eine separate skalare Variable (wie der andere Array-Typ), aber die Werte sind über einen Schlüssel, der ein beliebiger sklarer Wert sein kann. Um einen assoziativen Array, dessen Name mit % beginnt, zu erstellen, weisen wir ihm die Werte zu wie einem normalen Array, nur das abwechselnd der Schlüssel und der dazugehörige Wert angegeben werden.

%Woerter = ("joerg","mrd","oliver","mrd","besim","woerterbuch",
          "tobias","sgml","dorota","tei","cecilia","celex",
          "oliver S.","malaga","matthias","lexikon");
    
Um das geheime Wort von Dorota zu finden, verwenden wir Dorota als Schlüssel für den assoziativen Array %Woerter über einen Ausdruck wie beispielsweise $Woerter{" Dorota"}. Der sich ergebende Wert ist dann tei. Auch hier kann mit Variablen gearbeitet werden, so daß wenn $Person auf Dorota gesetzt wird, $Woerter{$Person} ebenfalls tei ergibt.
#!/opt/perl5/bin/perl
%Woerter = ("joerg","mrd","oliver","mrd","besim","woerterbuch",
          "tobias","sgml","dorota","tei","cecilia","celex",
          "oliver S.","malaga","matthias","lexikon");
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name eq "Joerg") ||
    ($Name eq "Oliver")) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      $GeheimesWort = $Woerter{$Name};
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while ($Versuch ne $GeheimesWort) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }
    
Schauen wir uns die Stelle an, an der das geheime Wort bestimmt wird. Wenn die Person nicht in %Woerter genannt wird, ist der Wert von $GeheimesWort undefiniert und sieht für den eq-Operator wie ein leerer String aus. Diesen können wir dann abfragen und einen Defaultwert für das geheime Wort setzen.
   [... Rest gelöscht ...]
      $GeheimesWort = $Woerter{$Name};
      if ($GeheimesWort eq "") {
        $GeheimesWort = "kokolores";
      }
      print "Wie lautet das geheime Wort? ";
   [... Rest gelöscht ...]
    

Verschiedene Eingaben behandeln

Wenn wir jetzt aber joerg statt Joerg, oder Oliver L. statt Oliver eingeben, werden wir zu den restlichen Benutzern gezählt, weil der Vergleich mit eq nur eine exakte Gleichheit abprüft.

Eine größere Flexibilität läßt sich mit regulären Ausdrücken erreichen. In Perl lautet der reguläre Ausdruck für einen String der direkt mit Oliver anfängt ^Oliver. Um die Übereinstimmung mit der Variablen $Name zu überprüfen, verwenden wir den m//-Operator (match):

if ($Name =~ m/^Oliver/) {
  # ja, es paßt
} else {
  # nein, es paßt nicht
}
    
Der reguläre Ausdruck wird dabei von zwei Schrägstrichen (slash - /) umgeben, zwischen denen Whitespaces, genauso wie zwischen Anführungszeichen (quotes - "), signifikant sind.

Um auch noch joerg zu akzeptieren schreiben wir hinter den zweiten Slash ein kleines i (ignore case), und um Oliverr nicht zuzulassen fügen wir den Wortgrenzenmarker \b (word boundary) hinzu, der festlegt, daß das Wort nach dem ersten r zu Ende ist. Im Skript sieht das dann folgendermaßen aus:

#!/opt/perl5/bin/perl
%Woerter = ("joerg","mrd","oliver","mrd","besim","woerterbuch",
          "tobias","sgml","dorota","tei","cecilia","celex",
          "oliver","malaga","matthias","lexikon");
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name =~ m/^joerg\b/i) ||
    ($Name =~ m/^oliver\b/i)) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      $GeheimesWort = $Woerter{$Name};
      if ($GeheimesWort eq "") {
        $GeheimesWort = "kokolores";
      }
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while ($Versuch ne $GeheimesWort) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }
    

Verschiedene Eingaben für alle Namen behandeln

Jetzt können Oliver und Joerg ihre Namen beliebig eingegeben, aber alle anderen müssen sich exakt an die un der Tabelle eingetragene Schreibung halten.

Um auch den anderen die einfachere Möglichkeit der Eingabe zu ermöglichen, müssen wir also zuerst alles nach dem ersten Wort entfernen und das erste Wort dann komplett in kleine Buchstaben umsetzen, bevor wir die Namen in der Tabelle nachschauen. Dies wird mit dem substitute-Operator und dem translate-Operator gemacht.

Mit dem s///-Operator suchen wir in $Name suchen wir das erste Zeichen welches kein Wort ist und werfen von diesem bis zum Ende des Strings alles weg. Der Befehl lautet:

$Name =~ s/\W.*//;
    
Hier steht der reguläre Ausdruck zwischen dem ersten und dem zweiten Slash, in dem \W, für ein Zeichen steht, welches kein Wort ist (kein Buchstabe, keine Ziffer und kein Unterstrich). Diesem folgt .*, wobei der Punkt für ein beliebiges und der Stern für beliebig viele Zeichen steht und zwar bis zum String-Ende. Zwischen dem zweiten und dem dritten Slash steht das, was die eben bestimmte Zeichenfolge ersetzen soll, in diesem Fall nichts.

Als nächstes müssen noch alle großen Buchstaben in $Name durch kleine mit dem tr///-Operator ersetzt werden. Der Befehl dafür lautet:

$Name =~ tr/A-Z/a-z/;
    
Hier stehen zwischen dem ersten und dem zweiten Slash die zu suchenden, zwischen dem zweiten und dritten die zu ersetzenden Zeichenlisten, wobei der Bindestrich zwischen a und z für alle Zeichen dazwischen steht. Diese beiden Zeilen bauen wir in das Skript ein.
#!/opt/perl5/bin/perl
%Woerter = ("joerg","mrd","oliver","mrd","besim","woerterbuch",
          "tobias","sgml","dorota","tei","cecilia","celex",
          "oliver","malaga","matthias","lexikon");
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
$OriginalName = $Name;
$Name =~ s/\W.*//;
$Name =~ tr/A-Z/a-z/;
if (($Name eq "joerg") ||
    ($Name eq "oliver")) {
      print "Hallo, $OriginalName! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $OriginalName!\n";
      $GeheimesWort = $Woerter{$Name};
      if ($GeheimesWort eq "") {
        $GeheimesWort = "kokolores";
      }
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while ($Versuch ne $GeheimesWort) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }
    
Zu beachten ist, daß wir wieder einen einfachen Vergleich bei den Namen durchführen, weil wir die Eingabe soweit verändern, daß ein exakter Vergleich wieder stimmt, und daß wir den Namen in der Variablen $OriginalName speichern, weil wir die Eingabe ja verändern, um sie vergleichen zu können, gleichzeitig aber das Original für die Begrüßung brauchen.

Modularität

Jetzt haben wir schon so viel zum Skriptcode hinzugefügt, daß wir eine Reihe von Zeilen lesen müssen, um die Arbeitsweise des Skripts zu verstehen. Jetzt sollten wir die übergeordnete Logik (Name erfragen, Schleife laufenlassen) von den Details (Vergleich des geheimen Worts) trennen. Dies dient einerseits der Übersichtlichkeit, andererseits können so mehrere Personen an demselben Skript arbeiten.

In Perl gibt es Unterprogramme (subroutines) welche Übergabeparameter und Rückgabewerte haben. Für unser momentanes Beispiel erstellen wir das Unterprogramm &GutesWort, das einen Namen und einen Rateversuch als Übergabeparameter nimmt und wahr oder falsch als Rückgabewert liefert.

sub GutesWort {
  my ($IrgendeinName,$IrgendeinVersuch) = @_;
  $IrgendeinName =~ s/\W.*//;
  $IrgendeinName =~ tr/A-Z/a-z/;
  if (($IrgendeinName eq "joerg") ||
      ($IrgendeinName eq "oliver")) {
    "wahr";
  } elsif (($Woerter{$IrgendeinName} || "kokolores" eq $IrgendeinVersuch) {
    "wahr";
  } else {
    "falsch";
  }
}
    
Bei der Definition eines Unterprogramms kommt also als erstes das Schlüsselwort sub, gefolgt vom Namen des Unterprogramms (ohne das Kaufmannsund & (ampersand)), gefolgt von einem Block Programmkode zwischen geschweiften Klammern. Die erste Zeile des Unterprogramms kopiert die Übergabeparameter aus dem Übergabearray @_ in die zwei Variablen $IrgendeinName und $IrgendeinVersuch die durch die my-Deklaration nur lokal in dem Unterprogramm existieren.

Dann wird der eingegebene Name wie inder letzten Version unseres Skripts gesäubert, und als letztes kommt ein if-elsif-else-Befehl, der entscheidet, ob das geratene Wort in $IrgendeinVersuch für den Namen in $IrgendeinName korrekt ist. Die Abfrage in dem elsif-Teil sieht etwas merkwürdig aus, weshalb wir sie etwas genauer untersuchen wollen.

($Woerter{$IrgendeinName} || "kokolores") eq $IrgendeinVersuch
    
Der erste Teil ist das bekannte Auffinden des geheimen Worts in dem assoziativen Array %Woerter mit dem Schlüssel, der in der skalaren Variablen $IrgendeinName steht. Darauf folgt der ||-Operator (logisches oder) und der String kokolores. Dies hat zur Folge, daß wenn das Auffinden des geheimen Worts in dem assoziativen Array einen Wert liefert, dieser mit dem Rateversuch verglichen wird, ansonsten der String kokolores.

Der letzte Ausdruck der in einem Unterprogramm ausgewertet wird ist der Rückgabewert. Also erhalten wir wahr, wenn der Name joerg oder oliver war, oder wenn das geheime Wort mit dem im Array für den entsprechenden Namen übereinstommt oder wenn es kokolores für alle anderen Namen lautet. Ansonsten liefert das Unterprogramm falsch.

Bauen wir also dieses Unterprogramm in unser Skript ein.

#!/opt/perl5/bin/perl
%Woerter = ("joerg","mrd","oliver","mrd","besim","woerterbuch",
          "tobias","sgml","dorota","tei","cecilia","celex",
          "oliver","malaga","matthias","lexikon");
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name =~ m/^joerg\b/i) ||
    ($Name =~ m/^oliver\b/i)) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while (! (&GutesWort($Name,$Versuch) eq "wahr")) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }

sub GutesWort {
  my ($IrgendeinName,$IrgendeinVersuch) = @_;
  $IrgendeinName =~ s/\W.*//;
  $IrgendeinName =~ tr/A-Z/a-z/;
  if (($IrgendeinName eq "joerg") ||
      ($IrgendeinName eq "oliver")) {
    "wahr";
  } elsif (($Woerter{$IrgendeinName} || "kokolores") eq $IrgendeinVersuch) {
    "wahr";
  } else {
    "falsch";
  }
}
    
Im Hauptprogramm sind wir wieder zum m//-Operator zurückgekehrt, weil der Name ja erst in dem Unterprogramm gesäubert wird. Aber der größte Unterschied ist in der while-Schleife. Diese enthält jetzt den Aufruf von &GutesWort mit den zwei Parametern $Name und $Versuch. Das Ergebnis welches der Aufruf liefert, wird mit dem String wahr verglichen und auf Gleichheit geprüft und dieses Ergebnis wird zum Schluß noch durch den !-Operator (logisches nicht) negiert. Die while-Schleife ließt sich also folgendermaßen:
Solange der Aufruf von &GutesWort mit $Name und $Versuch nicht gleich wahr ist, führe den Block aus.

Die Liste der geheimen Wörter steht in einer Datei

Angenommen die Liste der geheimen Wörter soll von drei Skripten verwendet werden. Dann müßten alle drei Skripten geändert werden, wenn sich ein geheimes Wort ändert. Also ist es schlauesten, die Liste in einer Datei zu speichern und sie einzulesen, wenn das Skript gestartet wird. Dazu brauchen wir einen Ein-/Ausgabe-Kanal, den sogenannten filehandle. In Perl gibt es standardmäßig schon drei Filehandles, nämlich STDIN, STDOUT und STDERR. STDINhaben wir sogar schon die ganze Zeit verwendet, um den Namen der Personen einzulesen. Nun ist es eine Kleinigkeit, einen anderen für unsere eigene Datei einzurichten.
sub InitialisiereWoerter {
  open (WORTLISTE, "wortliste");
  while ($Name = <WORTLISTE>) {
    chomp ($Name);
    $Wort = <WORTLISTE>;
    chomp ($Wort);
    $Woerter{$Name} = $Wort;
  }
  close (WORTLISTE);
}
    
Das Format dieser Wortlistendatei ist ein Eintrag pro Zeile, wobei Namen und geheime Wörter sich abwechseln. Für unsere gegenwärtigen Daten würde das ungefähr so aussehen:
joerg
mrd
oliver
mrd
besim
woerterbuch
tobias
sgml
dorota
tei
cecilia
celex
oliver
malaga
matthias
lexikon
    
Die open()-Anweisung erzeugt einen Filehandle namens WORTLISTE und verknüpft es mit der Datei wortliste im aktuellen Verzeichnis. Filehandles werden nicht wie Variablen durch ein spezielles Zeichen am Anfang gekennzeichnet, sondern werden immer nur mit Großbuchstaben geschrieben.

Die while-Schleife liest mit Hilfe des Filehandles WORTLISTE bei jedem Durchlauf zwei Zeilen aus der Datei wortliste und entfernt das letzte Zeichen. Die erste der beiden Zeilen wird in die Variable $Name geschrieben, die zweite in die Variable $Wort. Danach wird $Wort im assoziativen Array %Woerter unter dem Schlüssel $Name abgelegt.

Wenn alle Zeilen eingelesen wurden, bricht die while-Schleife ab und der Filehandle WORTLISTE wird mit der close()-Anweisung geschlossen.

#!/opt/perl5/bin/perl
&InitialisiereWoerter;
print "Wie lautet Dein Name? ";
$Name = <STDIN>;
chomp ($Name);
if (($Name =~ m/^joerg\b/i) ||
    ($Name =~ m/^oliver\b/i)) {
      print "Hallo, $Name! Wie nett von Dir mal wieder vorbeizuschauen!\n";
    } else {
      print "Hallo, $Name!\n";
      print "Wie lautet das geheime Wort? ";
      $Versuch = <STDIN>;
      chomp ($Versuch);
      while (! (&GutesWort($Name,$Versuch) eq "wahr")) {
        print "Falsch, versuch es noch einmal. Wie lautet das geheime Wort? ";
        $Versuch = <STDIN>;
        chomp ($Versuch);
      }
    }

sub GutesWort {
  my ($IrgendeinName,$IrgendeinVersuch) = @_;
  $IrgendeinName =~ s/\W.*//;
  $IrgendeinName =~ tr/A-Z/a-z/;
  if (($IrgendeinName eq "joerg") ||
      ($IrgendeinName eq "oliver")) {
    "wahr";
  } elsif (($Woerter{$IrgendeinName} || "kokolores") eq $IrgendeinVersuch) {
    "wahr";
  } else {
    "falsch";
  }
}

sub InitialisiereWoerter {
  open (WORTLISTE, "wortliste");
  while ($Name = <WORTLISTE>) {
    chomp ($Name);
    $Wort = <WORTLISTE>;
    chomp ($Wort);
    $Woerter{$Name} = $Wort;
  }
  close (WORTLISTE);
}
    

Vorher Zurück Weiter
Weiter: Siebte Sitzung Zurück: Unterlagen Vorher: Sechste Sitzung (Referat)
Oliver Lorenz Jörg Schreiber
zuletzt geändert am 14. Juli 1998