




Widmen wir den letzten Tag der Woche ein paar weiteren Beispielen. Sie werden in diesem Kapitel nicht viel Neues lernen, auch Übungen und Quiz gibt es heute nicht. Betrachten Sie es als kurze Verschnaufpause, in der Sie sich vollständige Perl-Skripts ganz in Ruhe ansehen können. Insgesamt hat das Buch drei dieser Beispiellektionen (immer am Ende der Wochen), die Ihr neues Wissen zementieren sollen.
Wir besprechen heute drei Perl-Skripts:
Verändern wir also noch einmal das Statistikprogramm, an dem wir die ganze Woche lang gearbeitet haben. Die Version von gestern erzeugte ein horizontales Histogramm, das etwa so aussah:
Haeufigkeit der einzelnen Zahlen:
1 | *****
2 | *************
3 | *******************
4 | ****************
5 | ***********
6 | ****
43 | *
62 | *
Heute wollen wir ein Diagramm mit vertikalen Balken ausgeben:
*
*
*
*
*
* *
* * *
* * * * * *
* * * * * * * *
* * * * * * * * *
* * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * * * * * * *
---------------------------------------
1 2 3 4 5 6 7 8 9 12 23 25 34 37 39 42
  Diese Diagrammform ist sehr viel schwieriger zu erstellen als die horizontale. Wir 
erstellen sie mit zwei verschachtelten for-Schleifen und großer Sorgfalt beim Zählen, 
damit auch alles an der richtigen Stelle landet.
  Noch etwas ist in dieser Version von stats.pl anders: Sie holt sich die Daten aus einer 
Datei, anstatt sie am Prompt vom Benutzer eintippen zu lassen. Wie bei allen Skripts, 
die aus einer Datei lesen, müssen Sie diese Datei beim Aufruf des Skripts in der 
Kommandozeile angeben:
% statsfinal.pl daten.txt
In der Datei (hier daten.txt) steht jede Zahl in einer einzelnen Zeile.

Sie finden daten.txt auch auf der beiliegenden CD, wenn Sie keine Lust haben, die Zahlen selbst einzutippen.
Listing 7.1 zeigt den Code unseres endgültigen Statistikskripts. So viel, wie wir bereits damit gearbeitet haben, sollte es ihnen vertraut sein. Konzentrieren Sie sich auf die beiden Teile des Skripts, die anders sind als in der letzten Version: die Eingabeschleife, die die Daten aus der Datei liest (Zeile 14 bis 21), und den Code zur Ausgabe des neuen Histogramms (Zeile 36 bis 49).
Listing 7.1: Das Skript statsfinal.pl
1: #!/usr/bin/perl -w
2:
3: $input = ''; # Benutzereingabe: Zahl
4: @nums = (); # Array: Nums;
5: %freq = (); # Hash: Zahl-Haeufigkeit
6: $maxfreq = 0; # hoechste Haeufigkeit
7: $count = 0; # Anzahl aller Nums
8: $sum = 0; # Summe
9: $avg = 0; # Durchschnitt
10: $med = 0; # Median
11: @keys = (); # temp Keys
12: $totalspace = 0; # gesamte Breite des Histogramms
13:
14: while (defined ($input = <>)) {
15: chomp ($input);
16: $nums[$count] = $input;
17: $freq{$input}++;
18: if ($maxfreq < $freq{$input}) { $maxfreq = $freq{$input} }
19: $count++;
20: $sum += $input;
21: }
22: @nums = sort { $a <=> $b } @nums;
23:
24: $avg = $sum / $count;
25: $med = $nums[$count /2];
26:
27: print "\nAnzahl der eingegebenen Nums: $count\n";
28: print "Summe der Nums: $sum\n";
29: print "Kleinste Zahl: $nums[0]\n";
30: print "Groesste Zahl: $nums[$#nums]\n";
31: printf("Durchschnitt: %.2f\n", $avg);
32: print "Mittelwert: $med\n\n";
33:
34: @keys = sort { $a <=> $b } keys %freq;
35:
36: for ($i = $maxfreq; $i > 0; $i--) {
37: foreach $zahl (@keys) {
38: $space = (length $zahl);
39: if ($freq{$zahl} >= $i) {
40: print( (" " x $space) . "*");
41: } else {
42: print " " x (($space) + 1);
43: }
44: if ($i == $maxfreq) { $totalspace += $space + 1; }
45: }
46: print "\n";
47: }
48: print "-" x $totalspace;
49: print "\n @keys\n";
  Da Sie den <>-Operator zum Einlesen von Daten aus Dateien bereits kennen, dürften 
die Zeilen 14 bis 21 Sie nicht überraschen. Beachten Sie, dass wir jede Zeile der Datei 
(also jede Zahl) der $input-Variablen zuweisen und diesen Wert dann im gesamten 
Block verwenden.
  Warum $input und nicht $_? Wir hätten auch $_ einsetzen können, aber viele der 
Anweisungen in diesem Block brauchen einen wirklichen Variablenbezug (sie greifen 
nicht von allein auf $_ zurück). In diesem Beispiel würde der Code durch $_ nur wenig 
kürzer, aber um einiges schwieriger zu lesen. Deshalb ist es hier die wohl bessere Idee, 
sich zugunsten der Lesbarkeit für eine eigene Variable zu entscheiden.

Je mehr Möglichkeiten ich in diesem Buch erkläre, desto mehr Möglichkeiten stehen Ihnen auch zur Auswahl - dass Perl ein besonderes Feature anbietet, bedeutet noch lange nicht, dass Sie es auch verwenden müssen. Man sollte immer abwägen zwischen sehr kurzem Code, den nur noch Perl-Experten entziffern können, und längerem, vielleicht gar weniger effizientem, aber dafür lesbarem Code. Betrachten Sie Ihr Perl-Skript als besonders gut gemacht, wenn jemand anderes es einfach durchlesen kann.
  Abgesehen davon, dass die Daten hier aus einer Datei anstatt von der 
Standardeingabe gelesen werden, unterscheidet sich dieser while-Block nur noch in 
einem Punkt von der letzten Version: Die neu hinzugefügte Anweisung in Zeile 18 
berechnet den Wert von $maxfreq, die maximale Häufigkeit. Diese gibt an, wie oft die 
Zahl vorkommt, die am häufigsten aufgetaucht ist. Mit diesem Wert legen wir später 
die Gesamthöhe des Diagramms fest. Hier vergleichen wir die maximale Häufigkeit 
lediglich mit der aktuellen und verändern $maxfreq, wenn letztere größer ist.
Weiter unten im Skript - nachdem wir sortiert, summiert und die ersten Ergebnisse ausgegeben haben - kommen wir zum Histogrammteil, der etwas einschüchternden Anhäufung von Schleifen in den Zeilen 36 bis 49.
  Ein Histogramm mit horizontalen Balken ist viel einfacher zu erstellen als eins mit 
vertikalen. Für das horizontale Histogramm brauchten wir vorgestern nur die Schlüssel 
im %freq-Hash zu durchlaufen und die entsprechende Zahl Sternchen auszudrucken 
(und das Ganze etwas zu formatieren). Für ein vertikales Histogramm müssen wir von 
Anfang an viel mehr Informationen über das Gesamtlayout parat haben, weil die 
Zeilen, die wir zeichnen, in keinem direkten Zusammenhang mit einem bestimmten 
Schlüssel oder Wert im Hash stehen. Außerdem müssen wir auch die Leerstellen für 
die Formatierung beachten.
  Wir bauen das Histogramm mit zwei Schleifen. Die äußere, eine for-Schleife, 
kontrolliert die Anzahl der auszugebenden Zeilen, das heißt die Höhe von oben nach 
unten. Die zweite, eine foreach-Schleife, bewegt sich innerhalb jeder Zeile von links 
nach rechts und schreibt entweder ein Sternchen oder ein Leerzeichen in jede Spalte. 
Mit diesen beiden verschachtelten Schleifen (der for-Schleife außen und der foreach-
Schleife innen) können wir uns von Zeile zu Zeile und von links nach rechts bewegen 
und sowohl die Höhe als auch die Breite des Histogramms von unseren Daten 
abhängig machen.
  Zuerst erstellen wir in Zeile 34 eine sortierte Liste aller Schlüssel des %freq-Hash, vor 
allem aus Bequemlichkeit und damit die for-Schleifen wenigstens etwas 
durchschaubarer werden.
  In Zeile 36 startet unsere äußere for-Schleife. Die Gesamthöhe des Diagramms wird 
von der am häufigsten auftretenden Zahl in unseren Daten bestimmt. Hier kommt der 
beim Einlesen ermittelte Wert von $maxfreq zum Einsatz. Die äußere for-Schleife 
beginnt bei diesem Höchstwert und arbeitet sich hinunter bis zur 0, wo sie abbricht. 
So erhalten wir die richtige Anzahl Zeilen.
  Die innere Schleife ist für die einzelnen Zeilen zuständig. Sie durchläuft die Schlüssel 
des %freq-Hash (also unsere Zahlen) und überprüft, ob in der aktuellen Zeile bei dieser 
Zahl ein * oder ein Leerzeichen gesetzt werden muss. Wir achten auch hier auf die 
Formatierung und fügen für die Spalten mit mehrstelligen Zahlen entsprechend mehr 
Leerstellen hinzu ($space wird für eine Zahl 333 einen anderen Wert haben als für 1).
Zeile für Zeile machen wir ab Zeile 38 folgendes:
$i, 
ist). Je weiter wir im Histogramm nach unten gehen, das heißt je kleiner $i wird, 
desto mehr Zahlen erfüllen dieses Kriterium.
   
 $i gleich $maxfreq ist - das heißt, wenn wir die erste Zeile des Diagramms 
erstellen -, addiert diese Schleife die Anzahl der in der aktuellen Spalte 
erforderlichen Leerstellen zur Variablen $totalspace. Nach dem ersten 
vollständigen Durchlauf der foreach-Schleife enthält $totalspace genau die 
Gesamtbreite des Histogramms.
   
 for-Schleife 
schließlich in Zeile 46 einen Zeilenvorschub aus - und beginnt alles wieder von 
vorn.
   
  Wenn wir die Sternchen des Histogramms ausgegeben haben, fehlen nur noch die 
Bezeichnungen für die Spalten. Hier simulieren wir mit Bindestrichen eine Querlinie 
(die Anzahl der erforderlichen Bindestriche haben wir bereits mit dem Wert von 
$totalspace berechnet) und schreiben unsere Zahlen darunter: einen String, der die 
Variable @keys interpoliert und alle Elemente von @keys, durch je ein Leerzeichen 
getrennt, ausgibt.
Kompliziert verschachtelte Schleifen sind häufig nur schwer nachzuvollziehen. Manchmal reicht eine Erklärung wie meine einfach nicht aus. Wenn dieses Beispiel Sie auch jetzt noch verwirrt, versuchen Sie, es mit ein paar kleinen Beispielzahlen selbst »durchzuspielen«. Verfolgen Sie Schritt für Schritt und Schleife für Schleife die »Entwicklung« der verschiedenen Werte - das hilft Ihnen, die Zusammenhänge zwischen den einzelnen Variablen klarer zu erkennen.
  Unser zweites Beispiel hat im wirklichen Leben nicht besonders viel Sinn, aber es 
zeigt ein paar komplexere Einsatzmöglichkeiten für if und while. Dieses Skript bittet 
Sie um die Eingabe einer einstelligen Zahl (und fängt Strings oder mehrstellige Zahlen 
ab). Dann schreibt es die Zahl als Wort aus, also 2 als zwei und 5 als fuenf und so 
weiter. Schließlich fragt es, ob Sie eine weitere Zahl eingeben möchten, und wenn ja, 
wiederholt es den gesamten Prozeß. Hier ein Beispiel, wie es ablaufen könnte:
% zahlenbuchstabierer.plGeben Sie die zu buchstabierende Zahl ein:buhKeine Strings. Eine Zahl von 0 bis 9 bitte.
Geben Sie die zu buchstabierende Zahl ein:45Zu hoch. 0 bis 9 bitte.
Geben Sie die zu buchstabierende Zahl ein::66 ist sechs.
Eine weitere Zahl versuchen (j/n)?:weiss nichtj oder n bitte.
Eine weitere Zahl versuchen (j/n)?:jGeben Sie die zu buchstabierende Zahl ein:33 ist drei.
Eine weitere Zahl versuchen (j/n)?:n
Listing 7.2 zeigt den kompletten Code.
Listing 7.2: Das Skript zahlenbuchstabierer.pl.
1: #!/usr/bin/perl -w
2: # zahlenbuchstabierer: buchstabiert Zahlen
3: # Simple Version, nur einstellige Zahlen
4:
5: $num = 0; # Zahl
6: $exit = ""; # Programm verlassen? j oder n.
7:
8: while ($exit ne "n") {
9:
10: while () {
11: print 'Geben Sie die zu buchstabierende Zahl ein: ';
12: chomp($num = <STDIN>);
13: if ($num ne "0" && $num == 0) { # wenn $num ein String
14: print "Keine Strings. Eine Zahl von 0 bis 9 bitte.\n";
15: next;
16: }
17: if ($num > 9) { # wenn $num mehrstellige Zahl
18: print "Zu hoch. 0 bis 9 bitte.\n";
19: next;
20: }
21: if ($num < 0) { # wenn $num negative Zahl
22: print "Keine negativen Zahlen. 0 bis 9 bitte.\n";
23: next;
24: }
25: last;
26: }
27:
28: print "$num ist ";
29: if ($num == 1) { print 'eins'; }
30: elsif ($num == 2) { print 'zwei'; }
31: elsif ($num == 3) { print 'drei'; }
32: elsif ($num == 4) { print 'vier'; }
33: elsif ($num == 5) { print 'fuenf'; }
34: elsif ($num == 6) { print 'sechs'; }
35: elsif ($num == 7) { print 'sieben'; }
36: elsif ($num == 8) { print 'acht'; }
37: elsif ($num == 9) { print 'neun'; }
38: elsif ($num == 0) { print 'null'; }
39: print "\n";
40:
41: while () {
42: print 'Eine weitere Zahl versuchen (j/n)?: ';
43: chomp ($exit = <STDIN>);
44: $exit = lc $exit;
45: if ($exit ne 'j' && $exit ne 'n') {
46: print "j oder n bitte.\n";
47: }
48: else { last; }
49: }
50: }
  Beginnen wir bei der äußeren Schleife, und arbeiten wir uns nach innen vor. Die erste 
while-Schleife, von Zeile 8 bis 50, enthält fast das gesamte Skript, denn sie soll, wenn 
am ja/nein-Prompt j eingegeben wird, auch fast das gesamte Skript noch einmal 
ausführen. Weil wir sie mindestens einmal laufen lassen möchten, muss die Bedingung 
in Zeile 8 am Anfang wahr sein (in weiser Voraussicht haben wir die Variable $exit 
dementsprechend initialisiert).
  Die zweite while-Schleife, von Zeile 10 bis 26, überprüft die Benutzereingaben. Das 
erste if stellt sicher, dass Sie keine Strings eingegeben haben (wenn $num nicht das 
Zeichen 0 ist, aber im numerischen Vergleich zu 0 ausgewertet wird, muss $num ein 
String sein). Das zweite if sieht nach, ob Sie eine Zahl größer 9 eingegeben haben 
(diese Version des Skripts buchstabiert nur einstellige Zahlen), und das dritte prüft auf 
negative Zahlen. Wenn eine dieser Bedingungen erfüllt ist, überspringen wir mit dem 
Schleifensteuerbefehl next den Rest des Blocks und gehen zurück zum Anfang der 
Schleife, in der wir uns unmittelbar befinden - das ist in diesem Fall immer noch die 
endlose while-Schleife in den Zeilen 10 bis 26. Wenn die Eingabe unseren Kriterien 
entspricht, also kein String und eine Zahl zwischen 0 und 9 ist, dann steigen wir mit 
last aus dieser while-Schleife aus und machen mit der ersten ihr folgenden 
Anweisung weiter.
  Diese Anweisung steht in Zeile 28. Wir geben den ersten Teil der Ergebnismeldung 
aus und gehen dann durch einen Satz von if- und elsif-Anweisungen, die für die 
aktuelle Zahl den entsprechenden String auf den Bildschirm schreiben. Dieser 
Abschnitt (Zeile 29 bis 38) wäre ein klassischer Fall für eine switch-Anweisung, mit 
der wir hier nicht immer wieder das gleiche eintippen müßten (den 
Programmiergöttern sei Dank für Copy-and-Paste).
  Sobald wir unser Zahlwort ausgegeben haben, fragen wir in der letzten while-Schleife 
(Zeile 41 bis 49), ob der gesamte Vorgang von vorn beginnen soll. Als Antwort 
erwarten wir ein j oder ein n. Beachten Sie den Aufruf der Funktion lc in Zeile 44 - 
wenn der Benutzer Großbuchstaben eingibt, wandelt lc sie vor der Überprüfung in 
Kleinbuchstaben um - damit akzeptieren wir also auch J oder N, ohne explizit darauf 
prüfen zu müssen.
  Wie Sie sehen, bestimmen wir hier nicht, was bei einem j oder n passieren soll, wir 
überprüfen nur, ob tatsächlich j oder n eingegeben wurde. Mehr ist gar nicht nötig. 
Sobald $exit einen gültigen Wert hat, endet die äußere while-Schleife, und wir 
kommen direkt zum Anfang, zur Schleifenbedingung in Zeile 8, zurück. Hier 
entscheiden wir über den weiteren Ablauf: Bei einem n bricht die äußere while-
Schleife wegen nicht erfüllter Schleifenbedingung ab, und das Skript ist beendet. 
Anderenfalls beginnen wir den Durchlauf von vorn und machen so lange weiter, bis 
der Anwender ein n eingibt.
Beenden wir dieses Kapitel mit einem etwas nützlicheren Skript: webseite.pl liest eine einfache Textdatei, fragt vom Benutzer ein paar grundlegende Einstellungen ab und spuckt dann eine HTML-Version der Datei aus. Dieses Skript ist kein besonders raffinierter HTML-Generator. Es läßt Sie lediglich Vorder- und Hintergrundfarbe, eine Überschrift sowie Ihre E-Mail-Adresse vorgeben, und im Text selbst fügt es nur Absatz-Tags an den richtigen Stellen ein. Aber Sie erhalten immerhin eine grundlegende HTML-Vorlage, mit der Sie weiterarbeiten können.
Bevor es Ihren Text in HTML konvertiert, bittet das webseite-Skript Sie um ein paar Angaben:
<TITLE>...</TITLE> in HTML)
   
 <H1>...</H1> in HTML)
   
 Ein Ablauf von webseite.pl könnte zum Beispiel folgendermaßen aussehen:
%webseite.pl heine.txtGeben Sie den Titel Ihrer Webseite ein:Heinrich Heine, Reisebilder, Dritter Teil
Geben Sie die Hintergrundfarbe ein (? zeigt Auswahl): ?
Zur Wahl stehen folgende Farben:
black (schwarz), maroon (dunkelbraun),
green (gruen), olive (olivgruen),
navy (dunkelblau), purple (violett),
teal (blaugruen), gray (grau),
silver (silbergrau), red (rot),
lime (hellgruen), yellow (gelb),
blue (blau), fuchsia (pink),
aqua (hellblau), white (weiss),
oder Return fuer keine Farbe
Geben Sie die Hintergrundfarbe ein (? zeigt Auswahl):whiteGeben Sie Textfarbe ein (? zeigt Auswahl):blackGeben Sie eine Ueberschrift ein:Kapitel XXVIGeben Sie Ihre E-Mail-Adresse ein:lemay@lne.com******************************
<HTML>
<HEAD>
<TITLE>Heinrich Heine, Reisebilder, Dritter Teil</TITLE>
</HEAD>
<BODY BGCOLOR="white" TEXT="black">
<H1>Kapitel XXVI</H1>
<P>
Die Natur wollte wissen, wie sie aussieht, und sie erschuf Goethe.
(... und so weiter, hier aus Platzgründen nicht aufgeführt).
<P>
Es liegt Wahrheit in diesen Worten und ich bin sogar der Meinung, dass Goethe manchmal seine Sache noch besser gemacht hätte als der liebe Gott selbst und dass er z.B. den Herrn Eckermann viel richtiger, ebenfalls mit Federn und grün erschaffen hätte.
<P>
<HR>
<ADDRESS><A HREF="mailto:lemay@lne.com">lemay@lne.com</A></ADDRESS>
</BODY>
</HTML>
Der fertige HTML-Code wird von diesem Skript nur auf den Bildschirm ausgegeben - das nützt Ihnen herzlich wenig, ich weiß - und bitte um etwas Geduld. Wie Sie Daten in eine Datei statt an die Standardausgabe leiten, behandeln wir an Tag 15. Bis dahin könnten Sie den hier ausgegebenen HTML-Text mit Copy-and-Paste in einen Texteditor übernehmen und als HTML-Datei speichern. Wenn Sie diese Datei dann in einen Webbrowser laden, sehen Sie das Ergebnis von webseite.pl. Unser Beispiel ergäbe etwa folgendes Bild:

Abbildung 7.1: Das Ergebnis des Skripts webseite.pl
Eine Bemerkung zu der Textdatei, die Sie an webseite.pl zum Konvertieren übergeben: Das Skript geht davon aus, dass diese aus Absätzen besteht, die durch Leerzeilen voneinander getrennt sind. Hier zum Beispiel der Inhalt der Datei heine.txt, die ich für das Beispiel oben verwendet habe:
Die Natur wollte wissen, wie sie aussieht, und sie erschuf Goethe. Sogar die Gedanken, die Intentionen der Natur vermag er uns widerzuspiegeln, und es ist einem hitzigen Goethianer, zumal in den Hundstagen, nicht zu verargen, wenn er über die Identität der Spiegelbilder mit den Objekten selbst so sehr erstaunt, dass er dem Spiegel sogar Schöpfungskraft, die Kraft, ähnliche Objekte zu erschaffen, zutraut.
Ein Herr Eckermann hat mal ein Buch über Goethe geschrieben, worin er ganz ernsthaft versichert: Hätte der liebe Gott bei Erschaffung der Welt zu Goethe gesagt: "Lieber Goethe, ich bin jetzt gottlob fertig, ich habe jetzt alles erschaffen, bis auf die Vögel und die Bäume, und du tätest mir eine Liebe, wenn du statt meiner diese Bagatellen noch erschaffen wolltest" - so würde Goethe, ebensogut wie der liebe Gott, diese Tiere und Gewächse ganz im Geiste der übrigen Schöpfung, nämlich die Vögel mit Federn und die Bäume grün, erschaffen haben.
Es liegt Wahrheit in diesen Worten und ich bin sogar der Meinung, dass Goethe manchmal seine Sache noch besser gemacht hätte als der liebe Gott selbst und dass er z.B. den Herrn Eckermann viel richtiger, ebenfalls mit Federn und grün erschaffen hätte. Es ist wirklich ein Schöpfungsfehler, dass auf dem Kopfe des Herrn Eckermann keine grünen Federn wachsen, und Goethe hat diesem Mann wenigstens dadurch abzuhelfen gesucht, dass er ihm einen Doktorhut aus Jena verschrieben und eigenhändig aufgesetzt hat.

Ich habe hier ausnahmsweise auf die ASCII-sichere Umschreibung von Umlauten und ß verzichtet - schließlich geht es bei Webseiten nicht zuletzt um die Ästhetik der Darstellung. Am Tag 1 habe ich erklärt, warum Ihr Perl-Interpreter damit nicht ohne weiteres umgehen kann. Wenn Sie diese Textdatei in dem Zeichensatz speichern, den Ihr Perl-Interpeter voraussetzt, werden Sie innerhalb Ihres Systems erst einmal keine Schwierigkeiten haben - beachten Sie jedoch, dass diese Datei auf dem Rechner Ihrer Brieffreundin in Kopenhagen (oder auch nur bei Ihrem Nachbarn, mit dessen Betriebssystem Sie nie arbeiten würden) zu ganz anderen, unerwünschten Ergebnissen führen kann. Übrigens haben Umlaute auch in HTML-Code eigentlich nichts zu.
Listing 7.3 zeigt den Code für unser Skript.
1: #!/usr/bin/perl -w
2: #
3: # webseite: simple Textdatei-zu HTML-Konvertierung
4: # *sehr* simpel. Keine Sonderzeichen, Links, Fett- oder
5: # andere Formatierungen, etc.
6: # Leerzeile in Textdatei == Absatz in HTML-Code
7:
8: $title = ''; # <TITLE>, Titel
9: $bgcolor = ''; # BGCOLOR, Hintergrundfarbe
10: $text = ''; # TEXT, Textfarbe
11: $head = ''; # <H1>, erste Ueberschrift
12: $mail = ''; # E-Mail-Adresse
13:
14: print " Geben Sie den Titel Ihrer Webseite ein: ";
15: chomp($title = <STDIN>);
16:
17: foreach $farbe ('Hintergrund', 'Text') { # laeuft zweimal: einmal
18: # je Farbe
19: $in = ''; # temp. Eingabe
20: while () {
21: print "Geben Sie die ${farbe}farbe ein (? zeigt Auswahl): ";
22: chomp($in = <STDIN>);
23: $in = lc $in;
24:
25: if ($in eq '?') { # Hilfe anzeigen
26: print "\nZur Wahl stehen folgende Farben:\n";
27: print "black (schwarz), maroon (dunkelbraun),\n";
28: print "green (gruen), olive (olivgruen),\n";
29: print "navy (dunkelblau), purple (violett),\n";
30: print "teal (blaugruen), gray (grau),\n";
31: print "silver (silbergrau), red (rot),\n";
32: print "lime (hellgruen), yellow (gelb),\n";
33: print "blue (blau), fuchsia (pink),\n";
34: print "aqua (hellblau), white (weiss),\n";
35: print "oder Return fuer keine Farbe\n\n";
36: next;
37: } elsif ($in eq '' or
38: $in eq 'black' or
39: $in eq 'maroon' or
40: $in eq 'green' or
41: $in eq 'olive' or
41: $in eq 'navy' or
42: $in eq 'purple' or
43: $in eq 'teal' or
44: $in eq 'gray' or
45: $in eq 'silver' or
46: $in eq 'red' or
47: $in eq 'lime' or
48: $in eq 'yellow' or
49: $in eq 'blue' or
50: $in eq 'fuchsia' or
51: $in eq 'aqua' or
52: $in eq 'white') { last; }
53: else {
54: print "Das ist kein gueltiger Farbname.\n";
55: }
56: }
57:
58: if ($farbe eq 'Hintergrund') { # Hintergrundfarbe
59: $bgcolor = $in;
60: } else { # Textfarbe
61: $text = $in;
62: }
63: }
64:
65: print "Geben Sie eine Ueberschrift ein: ";
66: chomp($head = <STDIN>);
67:
68: print "Geben Sie Ihre E-Mail-Adresse ein: ";
69: chomp($mail = <STDIN>);
70:
71: print '*' x 30;
72: # jetzt geht der HTML-Code los:
73: print "\n<HTML>\n<HEAD>\n<TITLE>$title</TITLE>\n";
74: print "</HEAD>\n<BODY";
75: if ($bgcolor ne '') { print qq( BGCOLOR="$bgcolor"); }
76: if ($text ne '') { print qq( TEXT="$text"); }
77: print ">\n";
78: print "<H1>$head</H1>\n<P>";
79:
80: while (<>) {
81: if ($_ eq "\n") {
82: print "<p>\n";
83: } else {
84: print $_;
85: }
86: }
87:
88: print qq(<HR>\n
89: <ADDRESS><A HREF="mailto:$mail">$mail</A></ADDRESS>\n);
90: print "</BODY>\n</HTML>\n";
Dieses Skript ist nicht besonders komplex oder syntaktisch raffiniert. Es verwendet nicht einmal Arrays oder Hashes (Wofür auch? Es gibt nichts, was hier gespeichert oder berechnet werden müßte) - nur viele, viele Schleifen und Bedingungen.
  Betrachten wir zuerst die große foreach-Schleife, die in Zeile 17 beginnt. Diese 
Schleife ist zuständig für die Frage nach Hintergrund- und Textfarbe. Weil sich diese 
beiden Eingabeaufforderungen exakt gleich verhalten, wollte ich denselben Code nicht 
für jede einzelne Aufforderung wiederholen (insbesondere, weil die if-Bedingungen in 
Zeile 37 bis 53 wirklich reichlich sind). Später werden Sie lernen, wie man sich 
derartig wiederholenden Code in eine Subroutine packt und dann lediglich diese 
Subroutine zweimal aufruft. Doch jetzt, wo wir viel über Schleifen, aber nichts über 
Subroutinen wissen, habe ich mich für eine foreach-Schleife entschieden.
  Die Schleife wird zweimal durchlaufen, einmal für den String 'Hintergrund' und 
einmal für den String 'Text'. Wir benutzen diese Strings für die Eingabeaufforderung 
und später zur Zuweisung der eingegebenen Werte an die entsprechende Variable 
($bgcolor oder $text).
  Innerhalb der foreach-Schleife haben wir eine andere, eine unendliche while-
Schleife, die die Eingabeaufforderungen so lange wiederholt, bis wir akzeptable 
Eingaben haben (Fehlerbehandlung ist immer eine gute Programmierübung). Der 
Benutzer kann 18 verschiedene Dinge eingeben: eine der sechzehn Grundfarben, ein 
Fragezeichen oder gar nichts.
  Die Bedingungen in Zeile 25 bis 55 durchlaufen jede dieser Möglichkeiten, zuerst die 
Eingabe von ?. Als Antwort auf ein Fragezeichen müssen wir lediglich eine hilfreiche 
Mitteilung ausgeben und dann mit next zum nächsten Durchlauf der while-Schleife 
springen (also, die Eingabeaufforderung neu anzeigen und auf weitere Eingaben 
warten).
  Die nächste Überprüfung (ab Zeile 37) stellt sicher, dass wir korrekte Eingaben haben, 
nämlich entweder einen Zeilenvorschub (dann haben wir eine leere Eingabe und legen 
keine Farbe fest1) oder eine der sechzehn Grundfarben. Beachten Sie, dass hier 
immer auf kleingeschriebene Farbnamen überprüft wird. In Zeile 23 haben wir mit der 
Funktion lc die Eingabestrings komplett in Kleinbuchstaben umgewandelt; deswegen 
kann der Benutzer auch BLACK oder Black eingeben - wir haben alle Möglichkeiten zu 
einer zusammengefaßt (aber bequemerweise die Eingabe von ? nicht berührt).
  Wenn die Eingabe einer dieser Vorgaben entspricht, steigen wir in Zeile 52 mit last 
aus der while-Schleife aus (Sie erinnern sich, next und last beziehen sich ohne 
Labels auf die nächste sie umgebende Schleife - hier also auf die while-, nicht auf die 
foreach-Schleife). Wenn uns die Eingabe nicht paßt, springen wir zum letzten else-
Fall in Zeile 53, geben eine Fehlermeldung aus und beginnen die while-Schleife von 
vorn.
  Die letzte Überprüfung in der foreach-Schleife (Zeile 58 bis 62) stellt fest, ob es sich  
bei der Eingabe um den Wert für die Hintergrund- oder die Textfarbe handelt, und 
weist den Farbwert dann der entsprechenden Variablen zu.
  Der Rest des Skripts, von Zeile 73 bis zum Ende, gibt den Anfang unserer HTML-Datei 
aus, liest und konvertiert die in der Kommandozeile angegebene Textdatei und setzt die 
abschließenden HTML-Tags ans Ende. Beachten Sie die Überprüfungen in Zeile 75 und 
76: Wenn $bgcolor und $text keine Werte haben, schreiben wir diese Attribute gar 
nicht erst in den HTML-Tag <BODY>. (Einfacher wäre es, sie dort stehen zu lassen, 
BGCOLOR="" oder TEXT="", aber das hätte nicht besonders nett ausgesehen.)
  Beachten Sie auch die Verwendung der Funktion qq. Sie haben qq an Tag 2 im 
Vertiefungsabschnitt kennengelernt. Mit der qq-Funktion erstellt man einen double-
quoted String, ohne doppelte Anführungszeichen zu verwenden. Wenn ich hier 
doppelte Anführungszeichen verwendet hätte, hätte ich vor die Anführungszeichen im 
String selbst einen Backslash setzen müssen. Ich finde, so sieht es besser aus.
  Die Zeilen 80 bis 86 lesen (mit <>) die Textdatei und geben dann einfach alles wieder 
aus, nur mit Absatz-Tags (<P>) statt Leerzeilen. Eine robustere Version dieses Skripts 
würde nach Sonderzeichen suchen (Akzenten, Umlauten und so weiter) und sie durch 
ihre entsprechenden HTML-Codes ersetzen - aber diese Aufgabe läßt sich mit Pattern 
Matching (Mustervergleich) viel einfacher lösen; deswegen heben wir uns das für 
später auf.
  Zum Abschluß bleibt nur noch die Ausgabe des E-Mail-Links (mit einem HTML mailto 
und Link-Tags) und der Tags zum Beenden der HTML-Datei (</BODY></HTML>).
Programmierlehrbüchern mangelt es selten an wortreichen Erklärungen, aber häufig an konkreten Beispielen. Ich möchte mich nicht um ausführliche Erklärungen drücken (wer lacht da?), aber diese Lektion - und die zwei weiteren an den Tagen 14 und 21 - zeigt Ihnen etwas längeren Code, der ohne thematische Trennung die Techniken aus den vorangegangenen Lektionen anwendet und vielleicht sogar eine mehr oder weniger nützliche Aufgabe erfüllt (obwohl ich nicht sicher bin, wie oft Sie eine Zahl werden buchstabieren müssen).
  Wir haben uns heute drei Skripts angesehen: statsfinal.pl, eine weitere Neuauflage 
des vertrauten Statistik-Skripts, gab - nach sorgfältigem Maßnehmen - mit Hilfe 
zweier verschachtelter for-Schleifen ein vertikales Histogramm aus. Das zweite, 
zahlenbuchstabierer.pl, arbeitete mit sehr vielen Bedingungen, um schließlich eine 
einstellige Zahl zu buchstabieren. Das dritte, webseite.pl, nahm in der 
Kommandozeile eine Textdatei entgegen, fragte nach ein paar Angaben und 
konvertierte den Inhalt der Textdatei in eine Webseite.
Herzlichen Glückwunsch, Sie haben die erste Woche dieses Drei-Wochen-Buches und bereits ein mächtiges Stück der Sprache bewältigt. Darauf bauen wir weiter auf. Auf zur Woche 2!





Die Farbwahl bleibt dann den Standardeinstellungen des Webbrowsers überlassen.