Woche 1

# Tag 3

#
Weitere Skalare und Operatoren

Ein Skalar ist, wie Sie seit spätestens gestern wissen, etwas Einzelnes wie eine Zahl oder ein String. Gestern haben Sie schon einiges darüber erfahren, was man mit skalaren Daten und Operatoren machen kann. Heute werde ich Ihnen noch etwas mehr zeigen und das Thema abschließen. Am Ende befassen wir uns mit ein paar verwandten Themen. Heute steht folgendes an:

- Verschiedene Zuweisungsoperatoren
- Stringverkettung und -wiederholung
- Rangfolge der Operatoren
- Ein kurzer Überblick über Ein- und Ausgabe

## Zuweisungsoperatoren

Gestern hatten wir bereits den grundlegenden Zuweisungsoperator `=` kennengelernt, der einer Variablen einen Wert zuweist. Zuweisungen verwendet man sehr häufig zur Änderung des Werts einer Variablen basierend auf ihrem aktuellen Wert.

    $inc = $inc + 100;

zum Beispiel macht genau, was man erwarten würde: Es nimmt den Wert von `$inc`, addiert `100` dazu und speichert das Ergebnis dann wieder in `$inc`. Derartige Operationen kommen so oft vor, dass es dafür eine Kurzschreibweise, einen eigenen Operator gibt: Die Variablenbezeichnung kommt auf die linke Seite, in die Mitte der Operator `+=` und auf die rechte Seite der Betrag, um den die Variable geändert werden soll:

    $inc += 100;

Perl hat solche »Schnellzuweisungen« für alle Rechenoperatoren, für die (bis jetzt noch nicht besprochenen) Stringoperatoren und sogar für `&&` und `||`. Tabelle 3.1 zeigt ein paar dieser Zuweisungsoperatoren. Im Grunde hat so gut wie jeder Operator, der zwei Operanden hat, eine solche Zuweisungsversion. Dabei gilt allgemein:

    Variable Zuweisungsoperator Ausdruck

ist äquivalent mit

    Variable = Variable Operator Ausdruck

Es gibt nur einen Unterschied zwischen den beiden Formen. In der langen Version wird die Variable zweimal ausgewertet, in der Kurzversion nur einmal. Meistens macht das beim Ergebnis des Ausdrucks keinen Unterschied, aber denken Sie daran, wenn Sie einmal unerwartete Ergebnisse erhalten.

Operator | Beispiel | gleichbedeutend mit
--- | --- | ---
`+=` | `$x += 10` | `$x = $x + 10`
`-=` | `$x -= 10` | `$x = $x - 10`
`*=` | `$x *= 10` | `$x = $x * 10`
`/=` | `$x /= 10` | `$x = $x / 10`
`%=` | `$x %= 10` | `$x = $x % 10`
`**=` | `$x **= 10` | `$x = $x**10`

Tabelle 3.1: Einige gebräuchliche Zuweisungsoperatoren

## Inkrement- und Dekrementoperatoren

Wie in C kann man mit den Operatoren `++` und `--` eine Variable um 1 inkrementieren bzw. dekrementieren, das heißt, 1 addieren bzw. subtrahieren. Und wie in C können beide Operatoren entweder als Präfix (vor der Variablen, also `++$x`) oder als Postfix (nach der Variablen, also `$x++`) gesetzt werden. Abhängig davon wird die Variable, bevor oder nachdem sie verwendet wurde, inkrementiert oder dekrementiert.

»Wie bitte?« denken Sie? Dann erkläre ich es Ihnen genauer: Die Operatoren `++` und `--` verwendet man mit einer Skalarvariablen, um den Wert der Variablen um 1 zu erhöhen oder zu erniedrigen - sie sind eine Art Abkürzung der `+=-`Operatoren oder der -=-Operatoren. Beide Operatoren können vor der Variablen stehen - das wird dann Präfixnotation genannt:

    ++$x

Oder nach der Variablen (Postfixnotation):

    $x++

Der Unterschied ist klein, aber fein: Die Position des Operators bestimmt, wann Perl beim Auswerten des Ausdrucks die Variable wirklich inkrementiert. Wenn Sie die Operatoren ganz alleinstehend - nur mit der Variablen, auf der operiert wird - verwenden, gibt es keinen Unterschied zwischen Postfix und Präfix. Die Variable wird inkrementiert, und Perl geht weiter. Wenn diese Operatoren aber auf der rechten Seite einer anderen Variablenzuweisung stehen, können Präfix- und Postfixnotation zu verschiedenen Ergebnissen führen. Schauen wir zum Beispiel auf das folgende Schnipsel Perl-Code:

    $a = 1;
    $b = 1;
    $a = ++$b;

Nach diesen Anweisungen haben sowohl `$a` als auch `$b` den Wert `2`. Warum? Die Präfixnotation bedeutet, dass der Wert von `$b` inkrementiert wird, bevor er `$a` zugewiesen wird. In diesem Ausdruck wird also zuerst `$b` auf `2` erhöht und dieser Wert dann `$a` zugewiesen.

Als Postfix sähe das anders aus:

    $a = 1;
    $b = 1;
    $a = $b++;

Auch hier wird `$b` inkrementiert und erhält den Wert `2`. Aber der Wert von `$a` bleibt `1`. In der Postfixnotation wird der Wert von `$b` nämlich verwendet, bevor er inkrementiert wird. Perl wertet `$b` als 1 aus, weist diesen Wert `$a` zu und erhöht erst dann `$b` auf `2`.

> Um ganz und gar korrekt zu sein: Meine Reihenfolge, wie die Dinge passieren, ist falsch. Bei einer Variablenzuweisung muss alles auf der rechten Seite des `=`-Operators vor der Zuweisung ausgewertet werden. In Wirklichkeit wird `$a` also bis zum allerletzten Schritt gar nicht verändert, sondern Perl merkt sich den ursprünglichen Wert von `$b,` inkrementiert ganz brav und greift dann erst bei der Zuweisung an `$a` auf den Originalwert von `$b` zurück. Aber wenn Sie nicht mit wirklich komplexen Ausdrücken arbeiten, können Sie sich das Ganze auch so vorstellen, dass die Zuweisung vor der Inkrementierung stattfindet.

## Stringverkettung und -wiederholung

String- und Textmanagement ist eine von Perls größten Stärken, und etliche der Beispiele in diesem Buch befassen sich mit der Arbeit mit Strings - in Strings suchen, Strings ändern, sie aus Dateien oder von der Tastatur lesen und sie an den Bildschirm, an Dateien oder über ein Netzwerk an einen Webbrowser schicken. Wir haben mit dem Thema erst angefangen und reden noch ganz allgemein über Strings.

Weil es ganz gut in die Lektion von heute paßt, möchte ich hier Perls Stringoperatoren erwähnen: `.` (Punkt) für Stringverkettung und `x` für Stringwiederholung

Anders als Java, das den `+`-Operator sowohl für die Addition als auch für die Stringverkettung nutzt, reserviert Perl `+` für die Verwendung mit Zahlen. Um zwei Strings aneinanderzufügen, verwenden Sie den `.`-Operator wie hier:

    'es war einmal' . ' vor langer Zeit';

Dieser Ausdruck erstellt einen dritten String: `'es war einmal vor langer Zeit'`. Er ändert keinen der Originalstrings.

Sie können auch mehrere Strings zu einem einzelnen langen String verketten:

    'eene ' . 'meene ' . 'muh... ' . 'und raus bist du!'

Der andere Stringoperator ist der `x`-Operator (nicht der `X`-Operator, es muss ein kleines x sein). Der `x`-Operator hat einen String auf der einen und eine Zahl auf der anderen Seite (aber wird beides bei Bedarf konvertieren) und erstellt dann einen neuen String: Der String links wird so oft wiederholt, wie die Zahl rechts vorgibt. Ein paar Beispiele:

    'blah' x 4;  # 'blahblahblahblah'
    '*' x 3;   # '***'
    10 x 5;   # '1010101010'

In diesem letzten Beispiel wird die Zahl `10` zum String `'10'` konvertiert und dann fünfmal wiederholt.

Wofür soll das gut sein? Nehmen Sie an, Sie bräuchten für ein Bildschirmlayout eine bestimmte Zahl von Leerstellen oder Füllzeichen, wobei es aber möglich sein soll, dass die Breite dieses Layouts variiert. Oder stellen Sie sich vielleicht eine Art ASCII-Kunst vor, wo die Wiederholung von Buchstaben besondere Muster produziert (das hier ist Perl, Sie dürfen so eigenartiges Zeug machen, nur zu). Sollten Sie jemals einen String wiederholen müssen, egal wie oft, kann der `x`-Operator das für Sie erledigen.

## Rangfolge und Assoziativität der Operatoren

Der Vorrang (precedence) der Operatoren legt fest, welche Operatoren in einem komplexen Ausdruck zuerst ausgewertet werden. Die Assoziativität bestimmt, wie gleichrangige Operatoren ausgewertet werden (linksassoziative Operatoren von links nach rechts, rechtsassoziative von rechts nach links, und bei nichtassoziativen Operatoren ist die Reihenfolge der Auswertung entweder nicht wichtig, nicht garantiert oder nicht einmal möglich). Tabelle 3.2 zeigt Vorrang und Assoziativität der verschiedenen Perl-Operatoren, wobei Operatoren mit höherem Vorrang (zuerst ausgewertet) in der Tabelle höher stehen als die mit niedrigerem Vorrang (später ausgewertet). Dies ist eine der Tabellen, die Sie bei der Arbeit mit Perl wahrscheinlich immer wieder brauchen werden - ich empfehle Ihnen, diese Seite wie auch immer zu markieren. Auswendig können müssen Sie das alles nicht, aber Sie sollten es schnell parat haben.

Sie können die Auswertungsreihenfolge eines Ausdrucks ändern, indem Sie Klammern setzen. Ausdrücke in Klammern werden vor denen außerhalb der Klammern ausgewertet.

Und noch etwas: In der Tabelle sind auch ein paar Operatoren aufgeführt, die Sie noch nicht kennen (und manche, die ich in diesem Buch nicht einmal behandeln werde). Ich habe bei den Operatoren, die ich später in diesem Buch noch erkläre, Verweise auf die entsprechenden Lektionen eingefügt.

Operator | Assoziativität | Bedeutung
--- | --- | ---
`->` | links | Dereferenzierungsoperator (Tag 19)
`++ --` | nicht | Inkrement und Dekrement
`**` | rechts | Exponent
`! ~ \ + -` | rechts | logisches NICHT, bitweises NICHT, Referenz (Tag 19), unäres +, unäres -
`=~ !~` | links | Mustervergleich (Tag 9)
`* / % x` | links | Multiplikation, Division, Modulo, String-Wiederholung
`+ - .` | links | Addition, Subtraktion, Stringverkettung
`<< >>` | links | Bitverschiebung nach links, nach rechts
monadische Operatoren | nicht | funktionsähnliche (benannte monadische) Operatoren
`< > <= >= lt gt lt le` | nicht | relationale Operatoren, Vergleiche
`== != <=> eq ne cmp` | nicht | Gleichheit (<=> and cmp, Tag 8)
`&` | links | bitweises UND
`| ^` | links | bitweises ODER, bitweises XOR
`&&` | links | logisches UND im C-Stil
`||` | links | logisches ODER im C-Stil
`..` | nicht | Bereichsoperator (Tag 4)
`?:` | rechts | Bedingungsoperator (Tag 6)
`= += -= *= /= etc.` | rechts | Zuweisungsoperatoren
`, =>` | links | Kommaoperatoren (Tag 4)
Listen-Operatoren | nicht | Listenoperatoren im Listenkontext (Tag 4)
`not` | rechts | logisches NICHT
`and` | links | logisches UND
`or xor` | links | logisches ODER, EXKLUSIV-ODER

Tabelle 3.2: Operatorvorrang und -assoziativität

## Ein Beispiel: Simple Statistik

Und jetzt ein Beispiel namens stats.pl, das Sie um die Eingabe von Zahlen bittet, eine nach der anderen. Wenn Sie mit der Eingabe fertig sind, gibt es aus, wie viele Zahlen es waren, sowie die Summe aller Zahlen und den Durchschnitt. Es ist ein einfaches Statistik-Skript, aber es zeigt Vergleiche und Variablenzuweisungen (und wir werden auf diesem Skript später aufbauen). Hier ein Beispiel, wie es aussieht, wenn es läuft:

    % stats.pl
    Bitte eine Zahl eingeben: 7
    Bitte eine Zahl eingeben: 4
    Bitte eine Zahl eingeben: 20
    Bitte eine Zahl eingeben: 1
    Bitte eine Zahl eingeben: 0.5
    Bitte eine Zahl eingeben:

    Anzahl der eingegebenen Zahlen: 5
    Summe der eingegebenen Zahlen: 32.5
    Durchschnitt: 6.50
    %

Listing 3.1 zeigt den Code des Statistikskripts.

> Wie beim Temperaturumrechnungsbeispiel von gestern gehe ich einfach davon aus, dass Sie nur Zahlen eingeben. Alles andere, aus Versehen oder Absicht, wird zu Perl-Warnungen und möglichen Fehlern führen. Später in dieser Woche lernen Sie mehr über die Eingabeüberprüfung. Im Moment nehmen wir einfach an, wir hätten nur gute Daten zu verarbeiten.

Listing 3.1: Das Skript stats.pl

    1: #!/usr/bin/perl -w
    2:
    3: $input = '';   # Benutzereingaben
    4: $count = 0;    # Zaehler
    5: $sum = 0;      # Summe
    6: $avg = 0;      # Durchschnitt
    7:
    8: while () {
    9:  print 'Bitte eine Zahl eingeben: ';
    10:  chomp ($input = <STDIN>);
    11:  if ($input ne '') {
    12:   $count++;
    13:   $sum += $input;
    14:   }
    15:  else { last; }
    16: }
    17:
    18: $avg = $sum / $count;
    19:
    20: print "\nAnzahl der eingegebenen Zahlen: $count\n";
    21: print "Summe der eingegebenen Zahlen: $sum\n";
    22: printf("Durchschnitt: %.2f\n", $avg);

Dieses Skript hat drei Hauptabschnitte: einen zum Initialisieren der Variablen, einen zum Einlesen und Speichern der Eingaben und einen zum Berechnen des Durchschnitts und zur Ausgabe der Ergebnisse.

Dies ist der Initialisierungsabschnitt:

    3: $input = '';   # Benutzereingaben
    4: $count = 0;    # Zähler
    5: $sum = 0;      # Summe
    6: $avg = 0;  # Durchschnitt

Vier Skalarvariablen werden verwendet: `$input`, um die Eingaben zu speichern, wie sie hereinkommen, `$count`, um die Anzahl der eingegebenen Zahlen zu verfolgen (wegen englisch count, »zählen«, heißen Zählervariablen häufig so). `$sum` und `$avg` sollen die Summe und den Durchschnitt speichern.

Im nächsten Abschnitt bitten wir um die Eingabe der Daten:

    8: while () {
    9:  print 'Bitte eine Zahl eingeben: ';
    10:  chomp ($input = <STDIN>);
    11:  if ($input ne '') {
    12:   $count++;
    13:   $sum += $input;
    14:   }
    15:  else { last; }
    16: }

Dieser Teil des Skripts liest mit einer `while`-Schleife und einer `if`-Bedingung immer wieder Eingabedaten ein, und zwar solange, bis wir eine Leerzeile erhalten. Ich werde Schleifen und Bedingungen erst am Tag 6 behandeln, lassen Sie sich davon also nicht aufhalten. Vielleicht haben Sie `while`s und `if`s ja auch schon mal gesehen und erkennen, dass die `while`-Schleife hier keine Abbruchbedingung hat (die Klammern in Zeile 8 sind leer) und dadurch eine unendliche Schleife ist.

Im Körper der `while`-Schleife lesen wir mit Zeile 10 die aktuelle Eingabe ein (und ich weiß, Sie wollen immer noch wissen, was `chomp` und `<STDIN>` eigentlich machen, wir kommen bald dazu). Zeile 11 überprüft, ob die Eingabe etwas enthält. Beachten Sie, dass es sich hier um einen String-, nicht um einen Zahlenvergleich handelt (`ne`).

Wenn Eingabedaten vorhanden sind, inkrementieren wir in Zeile 12 die Variable `$count` und addieren in Zeile 13 den neu eingegebenen Wert zur Variablen `$sum`. So halten wir den Zähler und die Gesamtsumme immer auf dem laufenden. Mit der Berechnung des Durchschnitts warten wir bis zum Ende der Schleife.

Zeile 15 enthält die `else`-Klausel der Bedingung in Zeile 11 - wenn die Eingabe leer war, dann machen wir direkt hier weiter und steigen mit dem Schlüsselwort `last` aus der unendlichen `while` -Schleife aus (Genaueres über `last` erfahren Sie, wenn wir uns am Tag 6 mit Schleifen befassen).

Zu guter Letzt beenden wir alles mit der Berechnung des Durchschnitts und der Ausgabe der Ergebnisse:

    18: $avg = $sum / $count;
    19:
    20: print "\nAnzahl der eingegebenen Zahlen: $count\n";
    21: print "Summe der eingegebenen Zahlen: $sum\n";
    22: printf("Durchschnitt: %.2f\n", $avg);

Zeile 18 ist einfach die Berechnung des Durchschnitts. Die Zeilen 20 bis 22 geben den Zähler, die Summe und den Durchschnitt aus. Beachten Sie das `\n` am Anfang der ersten `print` -Anweisung; es fügt eine extra Leerzeile vor der Ergebnisausgabe ein. Sie erinnern sich, dass `\n` überall in einem String stehen kann, nicht nur am Ende.

In der dritten Ergebniszeile verwenden wir wieder `printf` zur Formatierung der Zahlenausgabe. Diesmal haben wir ein `printf`-Format genommen, das den Wert als Fließkommazahl mit 2 Dezimalstellen ausgibt (`%.2f`). Im nächsten Abschnitt erfahren Sie mehr über `printf`.

## Ein- und Ausgabe

Wir werden den heutigen Tag mit zwei Themen abschließen, die anfangs nicht zu all dem zu passen scheinen, worüber wir bis jetzt im Zusammenhang mit Skalaren gesprochen haben: den Umgang mit Eingabe und Ausgabe, Input und Output. Ich komme hier aus einem wesentlichen Grund dazu: Sie sollen wissen, was in Ihren Skripts abläuft, wenn etwas von der Tastatur gelesen und auf den Bildschirm ausgegeben wird.

In diesem Abschnitt reden wir erst einmal über ganz einfache Eingabe und Ausgabe, im Laufe dieses Buches werden Sie mehr darüber erfahren, insbesondere auch über den Zugriff auf Dateien. Tag 15 schließlich ist ganz diesem Thema gewidmet.

### Datei-Handles und Standardein- und -ausgabe

Zuerst etwas Terminologie: In den Skripts von heute und gestern haben Sie mit Ihrem Perl-Code Eingabedaten von der Tastatur eingelesen und Ausgaben an den Bildschirm geschickt. Tastatur und Bildschirm sind aber nicht unbedingt die besten Formulierungen, denn im Grunde genommen lesen Sie von einer Quelle namens Standardeingabegerät (oft auch nur kurz Standard Input oder Standardeingabe genannt) und schreiben zu einem Ziel namens Standardausgabegerät (Standard Output, Standardausgabe). Diese beiden Konzepte sind von Unix-Systemen entliehen. Wenn Sie mit Unix vertraut sind, wird Ihnen die Verwendung von Pipes und Filtern und Redirection geläufig sein. Kennen Sie nur Windows oder Mac, ergibt der Gedanke an ein Standardeingabe- oder -ausgabegerät für Sie wahrscheinlich nicht viel Sinn.

In jedem Fall aber arbeiten Sie, wenn Sie Daten von einer Quelle lesen oder auf ein Ziel schreiben, mit sogenannten Datei-Handles (File-Handles). Datei-Handles beziehen sich meist auf Dateien auf einem Speichergerät (Festplatte, Diskette etc.), es kommt aber auch vor, dass Daten von einer namenlosen Quelle kommen oder dort hingehen, zum Beispiel von oder zu einem anderen Programm wie einem Webserver. Zur allgemeinen Handhabung von Datenquellen und -zielen, die keine Dateien sind, bietet Ihnen Perl vordefinierte Datei-Handles für die Standardeingabe und die Standardausgabe an: `STDIN` und `STDOUT` (es gibt auch `STDERR` für Standard Error, aber das heben wir für später auf). Mit diesen beiden Datei-Handles kann man Daten von der Tastatur holen (`STDIN` ist der normale Eingabekanal) und an den Bildschirm ausgeben (`STDOUT` ist der normale Ausgabekanal).

### Mit <STDIN> eine Zeile von der Standardeingabe lesen

In unseren bisherigen Skripts stand meistens eine Anweisung zum Einlesen der Tastatureingaben, die etwa so aussah:

    chomp($eingabezeile = <STDIN>);

So etwas wird Ihnen in Perl-Code häufiger begegnen, auch wenn es oft in mehreren Zeilen steht, etwa so (beide Formen sind äquivalent):

    $eingabezeile = <STDIN>
    chomp($eingabezeile)

Sie wissen, dass `$eingabezeile` eine Skalarvariable ist und dass Sie ihr etwas zuweisen. Aber was?

Der `STDIN` -Teil dieser Zeile ist der vordefinierte Datei-Handle für die Standardeingabe. Sie brauchen diesen speziellen Datei-Handle weder besonders zu öffnen noch irgendwie zu organisieren: er steht Ihnen einfach zur Verfügung. Warum er komplett großgeschrieben ist? Das ist eine Perl-Konvention, damit man Datei-Handles nicht so leicht mit anderen Sachen durcheinanderbringt (wie zum Beispiel Schlüsselwörtern der Sprache).

Die spitzen Klammern um `STDIN` sind nötig, um Eingaben aus einem Datei-Handle zu lesen. Die `<>`-Zeichen werden auch oft Eingabeoperator genannt (wegen ihrer Form manchmal auch Diamantoperator). `<STDIN>` bedeutet also: »Lies die Eingaben aus dem Datei-Handle `STDIN.`« In diesem besonderen Fall, wo Sie den `<STDIN>`-Ausdruck einer Variablen zuweisen, liest Perl eine Zeile von der Standardeingabe und stoppt, sobald es zu einem Zeilenvorschub (Newline) oder auf dem Macintosh zu einem Wagenrücklauf (Carriage Return) kommt. Anders als in C können Sie die Daten nicht explizit durchlaufen und jedes einzelne Zeichen daraufhin betrachten, ob es ein Zeilenende ist - Perl erledigt das für Sie. Alles, was Sie brauchen, ist `<STDIN>` und eine Skalarvariable, in der Sie die Eingabezeile dann speichern.

> Was für `<STDIN>` eigentlich eine Zeile ist, wird von Perls Input Record Separator festgelegt, dem Trennsymbol für Eingabedatensätze. Voreingestellt ist der Zeilenvorschub. Am Tag 9 erfahren Sie, wie man dieses Trennzeichen ändert. Bis dahin nehmen Sie einfach an, dass das Zeilenendezeichen auch tatsächlich das Ende einer Zeile ist.

Dieses ganze Gerede über Ein- und Ausgabe führt uns endlich auch zu der Funktion `chomp` (vom englischen chomp, »nagen«). Wenn Sie eine Eingabezeile mit `<STDIN>` einlesen und in einer Variablen speichern, erhalten Sie alles, was eingetippt wurde, inklusive dem Zeilenvorschub am Ende. Den wollen Sie normalerweise aber dort gar nicht haben, es sei denn, Sie geben die Eingabe gleich wieder aus und brauchen ihn zur Formatierung. Die Perl-Funktion `chomp` nimmt einen String, und wenn das letzte Zeichen ein Zeilenvorschub ist, entfernt es diesen. Beachten Sie, dass `chomp` den Originalstring direkt verändert (anders als Stringverkettung und andere Stringfunktionen, die komplett neue Strings erstellen und die ursprünglichen Strings unverändert lassen). Deshalb können Sie `chomp` für sich allein in einer eigenen Zeile aufrufen, ohne der Variablen, die den String enthält, den Wert neu zuweisen zu müssen:

    chomp($eingabezeile)

> Frühere Perl-Versionen hatten eine ähnliche Funktion für die gleiche Aufgabe, genannt `chop`. Wenn Sie älteren Perl-Code lesen, werden Sie auf viel `chop` stoßen. Der Unterschied zwischen `chomp` und `chop` ist, dass `chop` das letzte Zeichen im String entfernt, ohne zu unterscheiden, ob es ein Zeilenvorschub ist oder nicht. `chomp` hingegen ist sicherer: Es schneidet nichts ab außer einen Zeilenvorschub.

### Mit print auf die Standardausgabe schreiben

Sobald Sie Eingaben in Ihr Perl-Skript geholt haben, von `<STDIN>`, aus einer Datei oder von wo auch immer, können Sie mit diesen Eingaben machen, was Sie wollen. Irgendwann kommt dabei der Augenblick, wo Sie auch wieder Daten ausgeben möchten. Die zwei gebräuchlichsten Wege hierfür kennen Sie schon: `print` und `printf`.

Fangen wir mit `print` an. Die Funktion `print` kann beliebig viele Argumente übernehmen und schickt sie an die Standardausgabe (üblicherweise den Bildschirm). Bis jetzt haben wir immer nur ein Argument verwendet, aber Sie können auch mehrere Argumente übergeben, getrennt durch Kommata. Mehrere `print`- Argumente werden verkettet, bevor sie ausgegeben werden:

    print 'Guten Morgen!';
    print 1, 2, 3;    # gibt '123' aus
    $a = 4;
    print 1, ' ', $a;  # gibt "1 4" aus
    print 1, " $a";    # gibt auch "1 4" aus

> Genaugenommen sind die `print`-Argumente eine Liste, und es gibt einen Weg, zwischen Listenelementen besondere Zeichen ausgeben zu lassen. Sie werden morgen, am Tag 4, mehr darüber lernen.

Ich habe vorhin schon den Datei-Handle `<STDOUT>` als Möglichkeit aufgeführt, das Standardausgabegerät anzusteuern. Sie haben aber vielleicht bemerkt, dass wir Daten an den Bildschirm immer nur mit `print` ausgegeben haben, ohne uns jemals auf `<STDOUT>` beziehen zu müssen. Perl will Ihnen Zeit und Tipparbeit ersparen und geht deswegen davon aus, dass Sie die Standardausgabe benutzen wollen, wenn Sie `print` ohne einen expliziten Datei-Handle verwenden. Die beiden folgenden Anweisungen bewirken exakt das gleiche:

    print "Hallo Welt!\n" ;
    print STDOUT "Hallo Welt!\n";

Am Tag 15, wenn wir über Datei-Handles sprechen, die mit echten Dateien verbunden sind, erfahren Sie mehr über die lange Version von `print`.

### printf und sprintf

Zusätzlich zum alten Arbeitspferd `print` bietet Perl auch die Funktionen `printf` und `sprintf`. Diese beiden Funktionen sind äußerst nützlich für spezielle Formatierungen und Ausgaben von Zahlen. Sie arbeiten fast genauso wie die gleichnamigen Funktionen in C, aber seien Sie vorsichtig: `printf` ist bei weitem nicht so effizient wie `print`. Nehmen Sie also nicht einfach an, Sie können `printf` überall verwenden, weil Sie es so gewohnt sind. Verwenden Sie `printf` nur, wenn Sie dafür einen besonderen Grund haben.

Wie Sie seit gestern wissen, verwendet man die `printf`-Funktion, um formatierte Zahlen und Strings auszugeben, zum Beispiel an die Standardausgabe. `sprintf` formatiert einen String und gibt dann einfach nur den neuen String zurück, ist also geeigneter für Verschachtelungen innerhalb von Ausdrücken (tatsächlich ruft sogar `printf` für die Formatierungsarbeit `sprintf` auf).

An beide Funktionen, `printf` und `sprintf`, übergibt man zwei oder mehr Argumente: Das erste Argument ist ein String mit Formatierungscodes, das zweite und alle weiteren sind die Werte, auf die diese Codes angewendet werden sollen. Wir hatten vorhin ein Beispiel von `printf`, das eine Fließkommazahl auf zwei Dezimalstellen abgeschnitten hat:

    printf("Durchschnitt: %.2f", $avg);

Wir haben auch schon gesehen, wie man auf den nächsten ganzzahligen Wert abrundet:

    printf("%d Grad Celsius\n", $cel);

Gestern haben Sie zudem gesehen, wie man mit `sprintf` eine Fließkommazahl auf zwei Nachkommastellen beschränkt:

    $wert = sprintf("%.2f", $wert);

Die Formatierungscodes folgen denselben Regeln wie die C-Versionen (obwohl der Längenspezifikator `*` nicht unterstützt wird) und können recht komplex werden. Ein simpler Formatierungscode, den Sie in Perl verwenden könnten, hat folgende Syntax:

    %l.px

Wobei `x` für einen Code steht, der auf den Wertetyp verweist. Am interessantesten sind für Sie wahrscheinlich der Formatierungscode `d` zur Ausgabe von Integern (ganzzahligen Werten) und der Code `f` für die Ausgabe von Fließkommazahlen. Das `l` und das `p` im Formatierungscode sind beide optional. `l` bezieht sich auf die Anzahl der Zeichen, die der Wert im Ergebnisstring aufnehmen soll (aufgefüllt mit Leerzeichen, wenn der Wert des Ausdrucks weniger als `l` Stelle hat), und `p` auf die Anzahl der Nachkommastellen in einer Fließkommazahl. Alle Zahlen werden auf das entsprechende Format gebracht.

Hier einige typische Beispiele, wie `sprintf` oder `printf` eingesetzt werden könnte:

    $wert = 5.4349434;
    printf("->%5d\n", $wert);          # ->   5
    printf("->%11.5f\n", $wert);       # ->  5.43494
    printf("%d\n", $wert);             # 5
    printf("%.3f\n", $wert);           # 5.435
    printf("%.1f\n", $wert);           # 5.4

Stehen mehrere Formatierungscodes hintereinander, werden sie von links nach rechts ausgewertet, wobei jeder Formatierungscode durch ein Argument ersetzt wird (daher sollten Sie gleichviel Formatierungscodes wie Extraargumente haben):

    printf("Startwert: %.2f Endwert: %.2f\n", $start, $end);

Wenn in diesem Beispiel `$start` gleich `1.343` und `$end` gleich `5.33333` ist, wird die Anweisung folgendes ausgeben:

    Startwert: 1.34 Endwert: 5.33

Wenn Sie mit den `printf`-Formatierungscodes aus C nicht vertraut sind, finden Sie mehr darüber in der `perlfunc`-Manpage (oder der printf-Manpage).

## Eine Anmerkung zum Gebrauch von Funktionen

Jetzt, da Sie die Funktionen `print` und `chomp` kennengelernt und einen kleinen Blick auf andere Funktionen wie `oct` und `int` und `sprintf` geworfen haben, ist ein guter Zeitpunkt, zu erklären, wie Funktionsaufrufe funktionieren. Wenn Sie genau aufgepaßt haben, haben Sie vielleicht bemerkt, dass ich die `print`-Funktion folgendermaßen aufrufe:

    print "Hallo, Welt!\n";

Ich habe aber `chomp` und `printf` folgendermaßen aufgerufen:

    chomp($input = <STDIN>);
    printf("Durchschnitt: %.2f", $avg);

Einmal stehen die Funktionsargumente in Klammern, das andere Mal nicht. Was ist nun richtig? Die Antwort ist: beides. Klammern um die Funktionsargumente sind optional - solange Perl versteht, was Ihre Argumente sind, kommt es mit beiden Formen wunderbar zurecht.

Ich verwende in diesem Buch beide Varianten. Meine generelle Regel ist, dass ich keine Klammern setze, wenn eine Funktion ein einziges Argument entgegennimmt - `int` oder `oct` sind hierfür gute Beispiele. Wenn mehrere Argumente übergeben werden oder das Argument ein Ausdruck ist (wie häufig bei `printf` oder `chomp` der Fall), setze ich Klammern.

Je nachdem, womit Sie sich vertraut fühlen - und was Ihnen richtiger scheint - schaffen Sie sich ruhig Ihre eigene Regel für das Einklammern von Funktionsargumenten. Ich sollte allerdings erwähnen, dass die Regel »Klammern sind optional, solange Perl versteht, was Ihre Argumente sind« gelegentlich schwierig zu begreifen ist, besonders bei mehreren Argumenten und noch mehr, wenn einige davon eingeklammerte Ausdrücke oder Listen sind. Und als wäre das noch nicht komplex genug: Einige von Perls eingebauten Funktionen sind eigentlich gar keine Funktionen, sondern Operatoren und haben deswegen einen anderen Vorrang als echte Funktionsaufrufe (mehr darüber im nachfolgenden Vertiefungsabschnitt). Es gibt genaue Rangfolgeregeln, wie komplexe Funktionsaufrufe ausgewertet werden. Doch die einfachste und sicherste Lösung, wenn Perl Ihre Argumente zu ignorieren scheint oder unerwartete Ergebnisse produziert, sind Klammern um alle Argumente. Die Aktivierung von Perl-Warnungen kann ebenfalls beim Abfangen einiger dieser Probleme hilfreich sein.

## Vertiefung

Noch nicht genug von Strings und Zahlen? Sie wollen mehr? Kein Problem. Dieser Abschnitt behandelt ein paar andere bemerkenswerte Eigenheiten im Zusammenhang mit skalaren Daten, bevor wir mit Listen weitermachen.

Alle Operatoren werden in der perlop-Manpage besprochen, die Funktionen in der perlfunc-Manpage. Wie ich bereits erwähnt habe: Sie kommen zu all diesen Seiten mit dem Befehl `perldoc` oder über die Website `http://www.perl.com/CPAN-local/doc/ manual/html/pod`.

### Nützliche Zahlen- und String-Funktionen

Perl enthält eine recht kleine Anzahl an Funktionen für eine Vielzahl von Zwecken. Diese Funktionen sind in Anhang A zusammengefaßt; die perlfunc-Manpage beschreibt sie noch detaillierter. Insbesondere bietet Perl nützliche Funktionen für Zahlen und Strings, inklusive der in Tabelle 3.3 aufgelisteten. Wir werden einige davon in den folgenden Kapiteln noch genauer untersuchen, andere können Sie selbst erforschen.

Funktion | Argument(e) | Was sie liefert
--- | --- | ---
`abs` | WERT | Absolutwert von WERT
`atan2` | Y, X | Arkus-Tangens von Y/X
`chr` | ZAHL | Das Zeichen, das im ASCII-Zeichensatz für ZAHL festgelegt ist
`cos` | AUSDR | Cosinus von AUSDR in Bogenmaß
`exp` | AUSDR | e hoch AUSDR. Für allgemeine Potenzierung können Sie `**` nehmen
`int` | AUSDR | Gibt den Integerteil von AUSDR zurück
`index` | STRING, TEILSTRING [, POSITION] | Gibt die Position zurück, an der TEILSTRING zuerst im STRING auftaucht. Wenn Sie die POSITION festlegen, beginnt dort die Suche
`lc` | AUSDR | Wandelt AUSDR in Kleinbuchstaben um
`lcfirst` | AUSDR | Wandelt das erste Zeichen in AUSDR in Kleinbuchstaben um
`length` | AUSDR | Länge von AUSDR in Bytes
`log` | AUSDR | Logarithmus von AUSDR (natürlicher Logarithmus zur Basis e)
`ord` | AUSDR | ASCII-Wert des ersten Zeichens von AUSDR
`rand` | [AUSDR] | Zufallszahl zwischen 0 und AUSDR. Ohne AUSDR: zwischen 0 und 1
`reverse` | LISTE | Gibt in skalarem Kontext alle Zeichen der Liste »rückwärts« zurück
`rindex` | STRING, TEILSTRING [, POSITION] | Wie `index`, nur dass `rindex` von hinten anfängt: gibt die Position zurück, an der TEILSTRING zuletzt im STRING auftaucht
`sin` | AUSDRUCK | Sinus von AUSDR (in Bogenmaß)
`sqrt` | AUSDR | Quadratwurzel von AUSDR
`substr` | AUSDR, STARTPOS [, LAENGE] | Gibt einen Teilstring von AUSDR zurück, und zwar ab dem STARTPOS-ten Zeichen in AUSDR. Wenn Sie die LAENGE des Teilstrings nicht festlegen, erhalten Sie alle Zeichen ab STARTPOS
`uc` | AUSDR | Wandelt die Zeichen in AUSDR in Großbuchstaben um
`ucfirst` | AUSDR | Wandelt das erste Zeichen in AUSDR in einen Großbuchstaben um

Tabelle 3.3: Zahlen- und String-Funktionen

### Bitweise Operatoren

Perl bietet den üblichen Satz C-ähnlicher Operatoren zum Spielen mit Bits in Integer- Zahlen: `~`, `<<`, `>>`, `&`. | und ^. Auch für diese Operatoren gibt es Zuweisungsabkürzungen. Genaueres finden Sie in der perlop-Manpage.

### Die Operatoren cmp und <=>

Zusätzlich zu den Vergleichsoperatoren, die ich im Abschnitt über Vergleiche beschrieben haben, verfügt Perl noch über die Operatoren `<=>` und `cmp`. Ersterer ist für Zahlen, der zweite für Strings. Beide geben `-1`, `0` oder `1` zurück, je nachdem, ob der linke Operator größer ist als der rechte, die Operatoren gleich sind oder der rechte Operator größer ist als der linke. Diese Operatoren verwendet man meistens zum Sortieren, worüber Sie am Tag 8 mehr erfahren werden.

### Funktionen und funktionsähnliche Operatoren

Die in Perl eingebauten Funktionen bestehen aus zwei Gruppen: Funktionen und Operatoren, an die ein Argument übergeben wird und die nur wie Funktionen aussehen. Die funktionsähnlichen Operatoren stehen in der Mitte der Auswertungsrangfolge und verhalten sich in dieser Hinsicht wie Operatoren (während Funktionsaufrufe mit Klammern immer den höchsten Vorrang haben). Sehen Sie in der perlop-Manpage unter »Named Unary Operators« nach der Liste dieser Funktionen.

## Zusammenfassung

Heute haben wir uns weiter durch die skalaren Daten gearbeitet. Sie haben weitere Operatortabellen gesehen und die Operatoren für die Zuweisung an Variablen, zur Änderung der Werte von Variablen und für die Verkettung und Wiederholung von Strings kennengelernt. Sie haben auch etwas über die Rangfolge von Operatoren gelernt, die bestimmt, welcher Operator zuerst abgearbeitet wird, wenn Sie mehrere Operatoren in einem Ausdruck stehen haben.

Am Ende der heutigen Lektion haben wir die Ein- und Ausgabe behandelt, insbesondere die Verwendung von `<STDIN>`, um Daten in ein Perl-Skript hineinzubekommen, und die verschiedenen print-Funktionen, um sie wieder auszugeben. Und Sie haben bereits etwas über den Aufruf von Funktionen mit und ohne Klammern um die Argumente erfahren.

Folgende Perl-Funktionen wurden heute besprochen:

- `print` nimmt eine Liste von kommagetrennten Werten und Strings entgegen und gibt diese Werte an das Standardausgabegerät `<STDOUT>` aus.
- `printf` nimmt einen Formatierungsstring und beliebig viele Werte entgegen und gibt diese Werte entsprechend den Formatierungsanweisungen aus.
- `sprintf` macht dasselbe wie `printf`, außer dass es den formatierten String zurückgibt, ohne irgend etwas auszugeben.
- `chomp` mit einem String-Argument schneidet alle Zeilenvorschubzeichen von diesem String ab und gibt die Anzahl der entfernten Zeichen zurück.
- `chop` ist die ältere Version von `chomp`; es entfernt das letzte Zeichen vom String und gibt das gelöschte Zeichen zurück.

Schlagen Sie in der perlfunc-Manpage (siehe auch Anhang A) nach, um mehr über diese Funktionen zu erfahren.

## Fragen und Antworten

Frage:
Ich möchte einen String Buchstabe für Buchstabe durchgehen und zählen, wie oft der Buchstabe »t« vorkommt. Wie kann ich das in Perl bewerkstelligen?

Antwort:
Nun ja, Sie könnten sich mit einer `for`-Schleife und einer Stringfunktion Buchstabe für Buchstabe durch den String arbeiten, aber das wäre wohl ein ziemlicher Overkill (und würde Sie als C-Programmierer outen, der bei Strings noch an nullterminierte Arrays denkt). Strings sind eine der besonderen Stärken von Perl, das über eingebaute Mechanismen zur Suche in Strings verfügt. Darum brauchen Sie diese entsetzlichen Buchstabe-für-Buchstabe- Vergleiche gar nicht. Am Tag 9 werden Sie mehr über diese Suchmechanismen, das sogenannte Pattern Matching, lernen. Bis dahin verschwenden Sie nicht soviel Zeit mit dem Iterieren über Strings - es geht wirklich viel einfacher.

Frage:
Kann ich `printf` genauso verwenden wie in C?

Antwort:
Ja, das können Sie, aber Sie sollten sich wirklich angewöhnen, statt dessen mit `print` zu arbeiten. Die `print`-Funktion ist effizienter und hilft beim Ausgleichen von Abrundungsfehlern in Fließkommazahlen. Es ist (ganz ehrlich) in den allermeisten Fällen die bessere Idee, `print` zu verwenden und auf `printf` nur in bestimmten Fällen (beispielsweise zum Runden) zurückzukommen. Mit `printf` können Sie alle Formatierungsanweisungen aus der Sprache C verwenden, außer `*`.

Frage:
Warum ist es wichtig, dass manche Funktionen Funktionen und manche eigentlich Operatoren sind? Benehmen sich nicht alle gleich?

Antwort:
Nein. Funktionen und Operatoren verhalten sich leicht unterschiedlich. Funktionen haben zum Beispiel einen höheren Rang. Außerdem können Argumente an einen Operator auf Vorrangsbasis gruppiert werden (was zu unerwarteten Ergebnissen führen kann). In den meisten Fällen aber sollte der Unterschied zwischen einer Funktion und einem Operator Ihnen nicht den Schlaf rauben.

## Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

### Quiz

1. Was ist der Unterschied zwischen dem Postfix-Inkrementoperator (`$x++`) und dem Präfix-Inkrementoperator (`++$x`)?
2. Was legt der Operatorvorrang fest? Was besagt die Assoziativät?
3. Was ist ein Datei-Handle? Wofür braucht man Datei-Handles?
4. Definieren Sie Standardeingabe und -ausgabe. Wofür werden sie verwendet?
5. Was macht die Funktion `chomp`?
6. Was sind die Unterschiede zwischen `print`, `printf` und `sprintf`? Wann verwenden Sie welche Funktion?
7. Was bewirken die folgenden Operatoren?

           .
         **
         ne
         ||
         *=

### Übungen

1. Schreiben Sie ein Programm, das beliebig viele Zeilen und jede Art von Eingabe, die durch Drücken der Eingabetaste beendet wird, entgegennimmt (also ähnlich arbeitet wie das Programm stats.pl). Geben Sie die Anzahl der eingegebenen Zeilen zurück.
2. FEHLERSUCHE: Was ist an dem folgenden Code-Fragment falsch?

           while () {
        print 'Einen Namen eingeben: ';
        chomp ($input = <INPUT>);
        if ($input ne '') {
          $namen++;
       }
        else { last; }}

3. Schreiben Sie ein Programm, das mehrere Wörter aus verschiedenen Zeilen einliest und diese Wörter zu einem Gesamtstring zusammenfügt.
4. Schreiben Sie ein Programm, das einen String annimmt und ihn dann auf dem Bildschirm zentriert (gehen Sie von einer Zeile mit 80 Zeichen und einem String mit weniger als 80 Zeichen aus). Tipp: Die Funktion `length` liefert Ihnen die Länge eines Strings in Zeichen.

## Antworten

Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.

### Antworten zum Quiz

1. Präfixoperatoren inkrementieren den Wert einer Variablen vor seiner Verwendung, Postfixoperatoren danach.
2. Der Vorrang eines Operators bestimmt, welche Teile eines Ausdrucks zuerst ausgewertet werden, wenn dieser andere Ausdrücke enthält. Assoziativät bestimmt die Reihenfolge, in der Operatoren von gleichem Rang auswertet werden.
3. Ein Datei-Handle wird zum Einlesen bzw. Ausgeben von Daten von einer Quelle bzw. an ein Ziel verwendet, sei es eine Datei, die Tastatur, der Bildschirm oder ein anderes Gerät. Datei-Handles sind für Perl der normale Weg, mit Eingabe und Ausgabe und mit all diesen Geräten umzugehen.
4. Standardeingabe und Standardausgabe sind generell Eingabequellen und Ausgabeziele (das heißt nicht nur aus oder in Dateien). Sie werden meist verwendet, um Benutzereingaben von der Tastatur zu lesen oder etwas auf den Bildschirm zu schreiben.
5. Die Funktion `chomp` entfernt das Zeilenvorschubzeichen vom Ende eines Strings. Wenn dort kein Zeilenvorschub steht, macht `chomp` gar nichts.
6. Die `print`-Funktion ist der übliche Weg, etwas auf den Bildschirm oder andere Ziele auszugeben. Die Funktion `printf` gibt formatierte Strings auf bestimmte Ziele aus; `sprintf` formatiert einen String und liefert diesen formatierten String als Wert zurück anstatt ihn auszugeben.
7. Die Antworten sind:
8. `.` verkettet Strings
9. `**` erzeugt Exponentialzahlen
10. `ne` »ungleich« für Strings
11. `||` logisches ODER (C-Stil)
12. `*=` Multiplizieren und Zuweisen, dasselbe wie `$x = $x * $y`

### Lösungen zu den Übungen

1. Hier eine mögliche Antwort:

           #!/usr/bin/perl -w

       $input = ''; # Eingabe
       $zeilen = 0; # Zeilenzaehler

       while () {
        print 'Geben Sie etwas Text ein: ';
        chomp ($input = <STDIN>);
        if ($input ne '') {
          $zeilen++;
         }
        else { last; }
       }

       print "Zahl der eingegebenen Zeilen: $zeilen\n";

2. Das Datei-Handle für die Standardeingabe ist `STDIN`, nicht `INPUT`.
3. Hier eine mögliche Antwort:

           #!/usr/bin/perl -w

       $input = ''; # Benutzereingaben
       $satz = '';  # Ergebnis-Satz;

       while () {
        print 'Geben Sie ein Wort ein: ';
        chomp ($input = <STDIN>);
        if ($input ne '') {
          $satz .= $input . ' ';
         }
        else { last; }
       }

       print "ergibt den Satz: $satz\n";

4. Hier eine mögliche Antwort:

           #!/usr/bin/perl -w

       $input = "";  # Benutzereingaben
       $leer = 0;    # Leerstellen drum herum
       $laenge = 0 ; # Länge des Strings

       print 'Geben Sie etwas Text ein: ';
       chomp ($input = <STDIN>);
       $laenge = length $input;
       $leer = int((80 - $laenge) / 2);
       print ' ' x $leer;
       print $input;
       print ' ' x $leer . "\n";
       print '*' x 80;

© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH