In ersten Abschnitt des Tutorials werden werden die Grundlagen der PERL Programmierung behandelt:
Traditionsbewusst beginnen wir diesen Kurs mit einem Programm, das die Worte "Hello World" auf die Konsole schreibt. Dazu erzeugt man mit seinem Lieblingseditor eine Textdatei (hello.pl), die nur aus folgender Zeile besteht:
print "hello world !\n";
Auf der Kommandozeile wird dieses Perlscript mit dem Kommando
"perl hello.pl"
dem PERL -Runtime zur Ausführung übergeben:
work@netto: > perl hello.pl
hello world !
work@netto: >
Der PERL "Inerpreter" verwandelt den PERL Quelltext in der Datei "hello.pl" in Bytecode, der dann mit einer Geschwindigkeit ausgeführt wird, die etwa der Ausführungsgeschwindigkeit von kompiliertem C-Code entspricht. Strenggenommen ist also der PERL-Interpreter kein Interpreter sondern ein "Runtime-Environment". Anders als Shell- oder gängige BASIC Interpreter führt PERL die Syntaxprüfung und Übersetzung in einem einzigen Schritt --nicht komandoweise-- aus. Anders als bei der Java Virtual Machine ist ein direkter Zugriff auf den kompilierten Code über das Dateisystem nicht die Regel, sondern eher die Ausnahme.
Seit PERL Version 5.005 ist das "B-Module" Bestandteil der PERL
Distribution, womit der Zugriff auf den Parse Tree und die Ausgabe des Bytecodes relativ einfach
möglich geworden sind. ( Das "B" steht für "Backend")
Die Optionen des B-Modules umfassen die Ausgabe von
C-Programmtext, der mit einem C-Compiler in nativen Code umgewandelt werden kann,
die Analyse des Codes auf "gefährliche" Strukturen, die Analyse von Subroutinenaufrufen und
Variablengebrauch, sowie die Wiederherstellung von PERL-Code aus Bytecode.
Das B-Moduls wird hier allerdings nicht weiter behandelt, da dies den Themenumfang einer Einführung
sprengen würde.
Die PERL-Distributionen seit der Version 5.6 enthalten eine Einführung in die Arbeit mit diesem Modul,
die man mit perldoc perlcompile oder
man 1 perlcompile aufrufen werden kann, wenn man sich für dieses Modul interessiert.
PERL-Scripts (genauer : alle interpretierten Un*x Scripts) lassen sich aber auch bequemer ohne den expliziten Aufruf des Runtime-Environments ausführen. Dazu muss in der ersten Zeile des Scriptes hinter einem Kommmentarzeichen ('#' im Unix Jargon auch "she" genannt) und einem Ausrufezeichen ('!' auch "bang" genannt) der volle Pfadname des Runtime angegeben werden. Die komplette Konstruktion "#!/<Pfadname>" wird auch gerne als "shebang" bezeichnet. In diesem, und allen folgenden Beispielen wählen wir den verbreiteten Pfadnamen: "/usr/bin/perl":
#!/usr/bin/perl
#
# ein Kommentar.......
#
print "hello world !\n";
Der volle Pfadnamen des PERL-RT-Environments lässt
sich mit dem Kommando "which" durch den Aufruf:
"which perl"
bestimmen. Außer der Angabe des Runtime-Environmentss muss
auch dafür gesorgt werden dass das Script vom Runtime-Environment
gelesen und ausgeführt werden kann. Dazu sind mindestens
Lese und Ausführungsrechte für den Benutzer
erforderlich.
Da sich auf Un*x Maschinen der aktuelle Pfad aus Sicherheitsgründen meist nicht im Suchpfad befindet wird das Script (-anders als bei DOS Derivaten-) im Allgemeinen nicht automatisch gefunden, wenn es sich im aktuellen Verzeichnis befindet. Zum Aufruf des Scriptes im aktuellen Verzeichnis muss daher noch die Sequenz "./" als Abkürzung für das aktuelle Verzeichnis vorangestellt werden. Mit dem Aufruf
./hello.pl
lässt dich das Script dann direkt ausführen.
Neben dem shebang
#!/usr/bin/perl
der das Script als PERL-Script ausweist, enthält das Script noch eine Anweisung , die Zeile:
print "hello world \n";
die die Zeichenkette "hello world", gefolgt von einem Zeilenenumbruch ("/n") auf die Konsole schreibt. Anweisungen werden durch ein Semicolon ";" abgeschlossen, das nur bei der letzten Anweisung entfallen kann. Die gängige Anordnung der Anweisungen, bei der jede Anweisung in einer eigenen Zeile steht dient lediglich der Lesbarkeit, und ist nicht zwingend. Das Codefragment :
print hello; print world; print "\n";
Geht zwar auch durch den Compiler, fällt aber aus ästhetischen Gründen durch.
Das obige Script zeigt noch ein Element von Perlprogrammen: den Kommentar . Wie in der Unix-Shellprogrammierung werden Kommentare durch ein "she" '#' eingeleitet. Alle auf das '#' folgende Zeichen bis zum Ende der Zeile werden vom Runtime ignoriert, und können beliebige Kommentare enthalten.
Der Skalar ist der verbreitetste Datentyp in PERL. Skalare können gewöhnliche alphanumerische Zeichenketten (Strings), sowie Zahlen enthalten.
In der Gleichbehandlung von Zahlen und sonstigen Zeichenketten besteht ein Hauptunterschied zwischen PERL und streng typisierten Programmiersprachen wie PASCAL, C, Java, die unterschiedliche Datentypen für alphanumerische Zeichenketten und Zahlenwerte unterschiedlicher Länge und Genauigkeit (int, float, longint,.... ) bereitstellen. Bei der Deklaration einer Variablen muss und kann in PERL also nicht angegeben werden ob es sich um eine Zahl oder eine gewöhnliche Zeichenkette handelt. Das Runtime Environment muss bei jeder Operation aus dem Kontext heraus neu entscheiden worum es sich beim Wert der Variablen handelt. Das Script zahlOderZeichen.pl demonstriert diese Kontextüberprüfung:
#!/usr/bin/perl -w
#
# Zahl oder Zeichen ?
#
$string="eine Zeichenkette";
$zahl=0.123456;
#
print "\n leider kein Ergebnis: ", $string+$string , "\n" ;
print "\n Ergebnis: ", $zahl+$zahl , "\n" ;
#
Hier wird das PERL-Runtime Environment mit dem Schalter -w im
warnungsaktivem Modus gestartet,
in dem zahlreiche Diagnosemeldungen ausgegeben werden.
(Eine Übersicht über die Diagnosemeldungen erhält man durch Eingabe der Kommandos
man perldiag
oder
perldoc perldiag.
Es ist sicher keine schlechte Idee sich den Aufruf von PERL mit dem Schalter -w als
"persönliche Voreinstellung" anzugewöhnen.
Mit den Anweisungen:
$string="eine Zeichenkette";
$zahl=0.123456;
Werden zwei skalare Variablen mit den Namen string bzw. zahl definiert.
Gleichzeitig werden diese Variablen mit dem alphanumerischen Wert "eine Zeichenkette",
bzw dem Zahlenwert 0.123456 initialisiert. Mit den beiden folgenden Anweisungen
print "\n leider kein Ergebnis: ", $string+$string , "\n" ;
print "\n Ergebnis: ", $zahl+$zahl , "\n" ;
wird dann versucht das Ergebnis einer Addition der Variable mit sich selbst zu berechnen, und das Ergebnis auf die Konsole auszugeben. Dies führt im ersten Fall zu einem Fehler:
work@netto:~/perl/progs/very_basic > ./zahlOderZeichen.pl
Argument "eine Zeichenkette" isn't numeric in add at ./zahlOderZeichen.pl line 10.
im zweiten Fall zum korrekten Ergebnis:
leider kein Ergebnis: 0
Ergebnis: 0.246912
work@netto:~/perl/progs/very_basic >
Der Output von "zahlOderZeichen.pl" demonstriert weitere interessante Eigenschaften von PERL die man je nach Standpunkt als Bug oder Feature einstufen kann:
Beide Features sind interessant für kritische Anwendungen in der Prozessteuerung. Ersteres ist erfreulich, weil falsche Eingaben durch defekte Sensoren oder unvorsichtige Benutzer das Programm nicht zum Absturz bringen, letzteres ist unerfreulich, wenn aufgrund der Existenz eines Rückgabewertes im weiteren Programmverlauf Entscheidungen getroffen werden, die auf falschen Werten basieren.
Zum Umgang mit den eben beschriebenen Problemen bietet PERL ein Konzept
das mit den Exceptions moderner
objektorienterter Programmiersprachen (Delphi,C++,Java,...) vergleichbar ist.
Dazu wird das Schlüsselword eval
verwendet. Leider verbietet es sich aus Platz und Zeitgründen an dieser Stelle
genauer auf das eval Konzept einzugehen, weitergehende Informationen findet man
unter dem Schlüsselwort "eval" in der gängigen PERL-Literatur oder
in der online-Hilfe unter "perldoc perlfunc".
print "zeichenkette"*12;
erzeugen ?
Die Definition von skalaren Variablen ist denkbar einfach. Hierzu wird einfach der Variablenname angegeben und mit einem vorangestellen Dollarzeichen "$" wird die Variable als Skalar gekennzeichnet.
Das Script defineScalar.pl zeigt einige Beispiele:
# !/usr/bin/perl
#
# Variablendefinitionen und nicht-Definitionen
#
$var1=0;
$var2='eine Zeichenkette';
#
print "\n erste Variable : \n";
print $var1;
print "\n zweite Variable : \n";
print $var2;
print "\n dritte Variable : \n";
print $var3
#
#
Dies liefert den folgenden Output:
work@netto:~/perl/progs/very_basic > ./defineScalar.pl
erste Variable :
0
zweite Variable :
eine Zeichenkette
dritte Variable :
work@netto:~/perl/progs/very_basic >
Das PERL Runtime Environment interpretiert das erste Auftreten einer Sequenz "$variablenname" als Deklaration einer skalaren Variablen. Die Tatsache, dass die Variable "$var3" mit der Anweisung
print $var3 ;
angesprochen wird bevor der Variable ein Wert zugewiesen wurde
lässt das Runtime Environment dementsprechen unbeindruckt:
es wird einfach eine leere Zeichenkette auf die Konsole ausgegeben.
Nichtinitialisierte Variablen
stören also den Programmablauf nicht, können aber zu unerwünschten
Ergebnissen führen. Im warnungsaktiven Modus gibt PERL daher bei jeder
uninitialisierten Variable eine Fehlermeldung aus.
Noch uninitialisierte Variablen haben den (konstanten) Wert "undef".
Zur Abfrage und Manipulation existieren die Funktionen "defined" und "undef".
Mit dem Aufruf:
defined $myvar
wird getestet, ob die Variable myvar definiert wurde,
mit dem Aufruf
undef $myvar
wird der Wert der Variablem $myvar "ent-definiert", soll heißen:
auf den Wert "undef" zurückgesetzt.
PERL akzeptiert eine ganze Reihe von Zahlenformaten:
Fließkommazahlen, z.B:
-0.3 0, 3.1415 ,13
-3.0E-1, 0E0 3.1415E1 1.3E10
Ganzahlen, z.B:
-1 , 3, 6_5
-01, 03, 0101
-0x1, 0x3, 0x41
Oktalzahlen sind durch eine führende "0", Hexadezimalzahlen durch die Sequenz "0x" gekennzeichnet.
Zur Verbesserung (?) der Lesbarkeit können bei der dezimalen Notation an beliebigen Stellen Unterstriche eingefügt werden, z.B:
10_000_000 , 89_123_123.123_123
Als Erbstück der von PERL verwendeten Standard-C Bibliothek entsprechen die Mathematischen Operatoren in PERL völlig den mathematischen Operatoren in "C" :
Die folgenden binären Operatoren stehen zur Verfügung um zwei Zahlenwerte miteinander zu verknüpfen:
Addition (binär)
Subtraktion (binär)
Multiplikation
: / Division
Rest einer Division (Modulo)
Potenzbildung
Inkrement
Dekrement
Unäre Operatoren sind solche Operatoren, die nur einen Zahlenwert als Argument annehmen. Außerhalb der Informatik spricht man meistens von Funktionen.
positives Vorzeichen
negatives Vorzeichen
Absolutbetrag
arcus tangens (x/y), wobei x ungleich Null sein muss
Cosinus
Natürliche Exponentialfunktion
Konversion aus dem HEX Format
ganzzahliger Anteil einer Fliesskommazahl
Nätürlicher Logaritmus (x>0)
Konversion aus dem OCT Format
Wenn rand ohne Argumente aufgerufen wird liefert es einen (gleichverteilten) Zufallswert zwischen 0 und 1 zurueck. wird rand mit einem Argument aufgerufen, dann wird ein gleichverteilter Zufallswert zwischen 0 und dem Argument zurückgeliefert.
Sinus
Quadratwurzel wobei x gräßer oder gleich Null sein soll
Initialisiert den Zufallsgenerator neu. Sollte mit einem möglichst zufälligen Argument aufgerufen werden.
Da PERL ursprünglich für und auf Unix-kompatiblen Systemen entwickelt wurde kann PERL auf solchen Systemen auch auf alle Funktionen der Standard C-Library zugreifen (z.B tangens). Dies wird insbesondere C-Programmierer freuen. Diese Funktionen befinden sich im Standard PERL Modul POSIX. Eine Beschreibung aller POSIX-Funktionen erhält man durch die Eingabe von
perldoc POSIX
auf der Konsole.
Für viele Programmierer ist die effiziente Umgang mit Zeichenketten das Hauptargument für den Einsatz von PERL. Neben den "Druckbaren Zeichen" die von der Tastatur aus eingegeben werden können unterstützt PERL den ganzen erweiterten ASCII Zeichensatz. Zeichenketten (strings) bestehen aus einer Abfolge von ASCII Zeichen. Zwar lässt sich auch eine Anweisung wie:
$val = bareword ;
durch den Compiler schmuggeln, wenn die Zeichenkette jedoch nichtdruckbare Sonderzeichen oder Leerstellen enthalten soll, dann muss sie bei der Definition in Anführungszeichen eingeschlossen werden. Hierzu bestehen (wie in der Shellprogrammierung) prinzipiell zwei Möglichkeiten:
Einfache Anführungszeichen (Ticks)
$val='eine ganze Zeichenkette';
$val=' ein Tick : \' '; und ein Backslash : \\';
In Ticks eingeschlossene Zeichenketten werden bis auf zwei Ausnahmen ohne weitere Ersetzung verarbeitet. Die Ausgabe von nichtdruckbaren Sonderzeichen oder das Ersetzen von Variablenwerten innerhalb der Zeichenkette ist bei diesem Konstrukt nicht möglich. Die beiden Ausnahmenen sind das Tick, das mit einem Backslash maskiert werden muss, und der Backslash selber (von dem ohne diese Konvention u.U. nicht entschieden werden könnte, ob er nicht ein Tick ausmaskiert.
Bei eigenen ruchlosen Experimenten des Autors dieser Zeilen liess sich der Backslash in solchen nicht doppeldeutigen Situationen aber auch ohne Maskierung unterbringen... )
Ein beliebter Anfängerfehler ist es die falschen Ticks zu verwenden. Neben dem richtigen Tick, das sich auf einer deutschen Tastatur über dem she "#" befindet gibt es auch noch ein falsches Tick (accent). Die Verwendung des falschen Ticks in einem script äussert sich in der Fehlermeldung:
"Unrecognized character \264 at - line xxx"
Innerhalb von doppelten Anführungszeichen werden eine ganze Reihe von Sonderzeichen sowie alle skalaren Variablen ersetzt. Das folgende Script (quotation.pl) demonstriert die Ersetzung des nichtdruckbaren ASCII-Characters Nr 7 (Bell,Klingel) sowie einiger skalarer Variablen:
#!/usr/bin/perl -w
#
# Zeichenersetzung
#
# nichtdruckbares ASCII Sonderzeichen (char 007 = BEL)
# oktal:
#
$val1="ein Piepser: \07 \n";
print $val1;
#
# hexadezimal
#
$val2="ein Piepser: \x07 \n";
print $val2;
#
# Ersetzung von Variablen durch ihren Wert :
#
$v1="Bill";
$v2=" liebt ";
$v3=" Melissa ";
$v4=" nicht! \n";
print "$v1 $v2 $v3 $v4 ";
Dies liefert den folgenden Output:
ein Piepser:
ein Piepser:
Bill liebt Melissa nicht !
Wobei (bei geeignet konfigurierter Umgebung) nach dem Ausdruck de Zeichenkette "ein Piepser" noch ein Piepton (ASCII CHR Nr 7) erzeugt wird.
Alle Zeichen des erweiterten ASCII Zeichensatzes lassen sich mithilfe des Backslashes "\" und des ASCII-Zeichencodes erzeugen. Der Zeichencode kann in oktaler Notation, oder nach einem vorangestellten "x" in hexadezimaler Form angegeben werden :
\101 "A" (Character Nr 65)
\x41 ("A" Character Nr 65)
Für einige, besonders häufig gebrauchte Zeichen gibt es auch noch bequemerer Backslashsequenzen, so das man nicht unbedingt deren ASCII-Code wissen muss :
doppeltes Anführungszeichen
backslash \
neue Zeile (newline)
Return
neue Seite (form feed)
horizontaler Tabulator
vertikaler Tabulator
Rückschritt (backspace)
akustisches Signal
Escape
Control-Zeichen (hier: Ctrl-C)
Schließlich gibt es auch noch Backslash-Sequenzen die die Groß/Kleinschreibung beeinflussen:
nächster Buchstabe klein
nächster Buchstabe groß
(Lowercase) Alle zwischen dem \L und einem schließenden \E eingeschlossenen Buchstaben werden kleingeschrieben.
(Uppercase) Alle zwischen dem \U und einem schließenden \E eingeschlossenen Buchstaben werden großgeschrieben.
siehe \L und \U)
Beispiele:
print "\UgrosS\LKLein": GROSSklein print "grosses \ug kleines \lK ": grosses G kleines k
Die PERL Onlinehilfe listet unter perldoc perlfunc etwa 30 eingebaute
Funktionen auf, die zur Manipulation von Strings verwendet werden können.
Die ausgefeilten Möglichkeiten zum Suchen und Ersetzen die Perl bietet werden hier unter dem
Stichwort
Regular Expressions beschrieben.
Im folgenden gehen wir auf eine Auswahl von besonders häufig benutzten Stringfunktionen
noch genauer ein:
$string1.$string2
Concatenationsoperator.
Hängt zwei Zeichenketten aneinander
chop($string)
Schneidet das letzte Zeichen einer Zeichenkette ab.
Liefert das abgeschnittene letzte Zeichen,
also nicht den String zurück.
chomp($string)
Schneidet die (systemspezifischen)
"Input Record Seperators"
vom Ende des Strings ab. Insbesondere brauchbar um eventuelle
abschließende newlines abzuschneiden.
Liefert 1 wenn Zeichen abgeschnitten wirden, sonst 0,
also insbesondere nicht den
beschnittenen String zurück.
index($substring,$string)
Liefert den Index des ersten Auftauchens
der Zeichenkette $substring innerhalb
der Zeichenkette $string. ermöglicht
Suchoperationen für Programmierer die den
Einsatz von Regular Expressions scheuen
(sogenannte Warmduscher).
Die Indices der Zeichen innerhalb einer
Zeichenkette
beginnen in der Regel mit dem Index 0.
Diese Voreinstellung
Kann durch Manipulation der vordefinierten
Variablen $dollar;[
verändert werden,
was aber meistens nicht ratsam ist.
lc ($string)
lowercase
Liefert einen String zurück,bei dem
alle Grossbuchstaben in Kleinbuchstaben
verwandelt sind.
uc ($string)
uppercase
Liefert einen String zurück,bei dem
alle Kleinbuchstaben in Grossbuchstaben
verwandelt sind.
length($string)
Liefert die Anzahl der Zeichen in $string zurück
Das Script "stringFunctions.pl", das diese Funktionen demonstriert, sei hier ohne Kommentar wiedergegeben:
#!/usr/bin/perl
#
#
# Konkatenation
print "\ Konkatenation : \n";
$s1="ersterString";
$s2="zweiterString";
print $s1 . $s2 ;
print "\n\n\n";
#
# chop
#
print "chop :\n";
$s3="012345678";
print chop($s3);
print "\n $s3 \n";
#
# chomp
#
print "chomp :\n";
$s4="012345678 \n \n \n \n";
print chomp($s4);
print "\n $s4 \n";
#
# index
#
print "index :\n";
$s5="012345678";
print index($s5,"45");
print "\n" ;
print index($s5,"52");
print "\n\n" ;
#
# length:
#
print "length : \n";
$s6="0123456789";
print length($s6);
print "\n\n";
#
# lc und uc
#
print "lc und uc \n";
$s7="Gross oder Klein ? \n";
print lc($s7);
print uc($s7);
print "$var2";?
Wie können Sie den Wert der
Variable $var1 unmittelbar
gefolgt von der Ziffer "2"
ausgeben lassen?
Eine Liste in PERL ist eine geordnete Sammlung von Skalaren.
Die Begriffe "Liste" und "Array" werden im Folgenden weitgehend synonym gebraucht,
auch wenn es feinsinnige Unterschiede zwischen "Liste" und "Array" gibt.
Prägnant gesagt ist eine Liste eine geordnete Menge von Werten
während ein Array eine Variable ist.
In der PERL FAQ Nr 4 (Data Manipulation) wird die Frage
What is the difference between a list and an array? noch ein wenig intensiver diskutiert.
Diese FAQ lässt sich mit dem Kommando perldoc perlfaq4
online aufrufen.
Listen in PERL bieten den vollen Komfort von Arrays (wahlfreier Zugriff über Indices), wie bei den dynamischen Datenstrukturen in PASCAL oder "C" muss aber die Anzahl der Elemente eines solchen "Listenarrays" nicht zum Zeitpunkt des Programmentwurfes feststehen, sondern kann im Rahmen der Beschränkung durch den zur Verfügung stehenden Hauptspeicher beliebig groß werden. Probleme mit der Speicherverwaltung durch "harte Zeiger" wie bei den vorgenannten Programmiersprachen werden in PERL weitgehend vermieden, da sich das Runtime Environment im Hintergrund um die Allokation und insbesondere Disallokation von Speicherplatz kümmert.
Der Klammeraffe "@" spielt für Arrays die gleiche Rolle, die das Dollarzeichen für Skalare spielt: Arrays werden durch vorangestellte Klammeraffen gekenzeichnet. Listen können durch eine simple Aufzählung der Elemente definiert werden:
@myarray= ("element1","element2","element3","element4");
Natürlich können auch Zahlenwerte durch Aufzählung in die Liste
aufgenommen werden:
@myarray= ("element1","element2","element3","element4", -1e23, 3.1415 );
Eine komfortable Variante z.B. zum Parsen von Daten
ist die Erzeugung eines Arrays mit der "qw" Funktion,
die ein Array aus einem String erzeugt indem sie die Grenzen zwischen den Elementen
über den Leerraum bestimmt:
@myarray= qw(" element1 element2 element3 element4 -1e23 3.1415 ");
Der Zugriff auf die Elemente der Liste erfolgt einfach über den Index.
$wert=$myarray[2];
für die Abfrage, bzw:
$myarray[2]=AUSDRUCK;
für die Manipulation eines einzelnen Feldes.
Ein Zugriff auf ein noch nicht definiertes Feld liefert den Wert
"undef", eine Zuweisung an einen Ausdruck vom Typ "$myarray[index]"
führt bei einem nicht existierenden Array "myarray" automatisch dazu,
das dass Array angelegt wird.
Solange sich niemand an der Variablen "$[" zu schaffen gemacht hat
(wovon wir im Folgenden immer ausgehen), beginnen die Indices eines Arrays
(wie in "C" und JAVA) mit 0. Zu jedem Array "@myarray" existiert
eine Variable "$#myarray", die den Index des letzten Elementes enthält.
Das Script "arrayInit.pl" demonstriert das implizite Anlegen eines Arrays, und das anschließende Abfragen des höchsten Index:
#!/usr/bin/perl -w
#
# Implizite Definition eines Arrays
# durch Wertzuweisung
#
$myarray[23]="ein Wert";
$myarray[12]="noch ein Wert";
print "Höchster Index in \@myarray : $#myarray";
#!/usr/bin/perl -w
#
# Aneinanderhaengen zweier Arrays
#
@headarray=( 1 , 2 , 3 );
@tailarray=( 4 , 5 , 6 );
push (@headarray,@tailarray);
print "\n";
print @headarray;
print "\n";
splice(@alist, $offset, $length, @insertlist)
ersetzt diejenige Teilliste von alist, die beim Index offset beginnt, und
$length Elemente lang ist durch die Liste @insertlist und liefert die
ersetzte Teilliste zurück. Die Angabe des Parameters "@insertlist" kann
auch entfallen, wenn lediglich Elemente aus einer Liste entfernt werden sollen.
Mit geeignet gewählten Parametern lassen sich die Funktionen
shift,unshift,push und
pop durch splice simulieren.
Das Script "splice.pl" zeigt ein Beispiel für die Funktion von splice:
#!/usr/bin/perl -w
#
@list=("a","b","c","d","e","d");
@insertlist=( 1,2,3,4,5,6,7,8,9,0);
#
print "\n\n Rueckgabewert von splice : \n";
@ar=splice(@list,2,2,@insertlist);
print @ar;
#
print "\n\n Manipulierte Liste : \n";
print (@list);
#
Das Script liefert die Ausgabe:
Rueckgabewert von splice :
cd
Manipulierte Liste :
ab1234567890ed
@myar=(1,2,3,4,5);
print join("--->",@myar);
liefert die Ausgabe:
1--->2--->3--->4--->5
$str übergebene Zeichenkette
in Teilstücke, die jeweils durch Teilstrings voneinander getrennt sind auf
die der reguläre Ausdruck "pattern" passt. Die Teilstücke werden
in die Felder eines Arrays geschrieben, das als Ergebnis zurügeliefert wird.
Als Vorgeschmack auf den
Abschnitt über reguläre Ausdrücke
zerlegt das Script "split.pl" eine Zeichenkette, wobei zuerst der
Zeilenumbruch (zugehöriger reg. Ausdruck : \n)
und anschließend eine beliebige Anzahl von Whitespaces
(zugehöriger reg. Ausdruck : \s.) als Trennmuster verwendet werden:
#!/usr/bin/perl -w
#
# Zerlegen nach einfachem newline Zeichen (\/n\)
#
$toSplit="token token token token \n token token ";
@ar=split(/\n/,$toSplit);
print "\n Zerlegung nach newline : \n ";
print ("Anzahl der Felder : ", $#ar + 1);
#
# Zerlegen nach beliebiger Anzahl von Whitespaces : (/\s/)
#
$toSplit="token token token token \n token token ";
@ar=split(/\s./,$toSplit);
print "\n Zerlegung nach whitespaces : \n ";
print ("Anzahl der Felder : ",$#ar + 1);
print "\n\n\n";
Insbesondere das Zerlegen nach Whitespaces tritt in der Praxis
häufig auf, wenn unstrukturierte Benutzereingaben gescannt werden müssen.
splice" um die Teilliste von @a die vom 3ten
bis zum 4ten Element reicht durch die Liste @b zu ersetzen.
Geben Sie die zusammengesetzte Liste und das herausgenommene Teilstück
auf die Konsole aus.
(Beispiel in 'datatypes/splice.pl')
Viele eingebaute Funktionen liefern unterschiedliche Ergebnisse zurück, je nachdem als Ergebnis ein Skalar (sogenannter "skalarer Kontext") oder eine Liste ("Listenkontext") erwartet wird. Das Script:
$scal = localtime ();
@list = localtime ();
print "\n\n Ergebnis als Skalar : \n" ;
print "$scal";
print "\n\n Ergebnis als Liste : \n";
print "@list";
erzeugt die Ausgabe:
Ergebnis als Skalar :
Sun Jun 11 10:24:06 2000
Ergebnis als Liste :
6 24 10 11 5 100 0 162 1work@netto:~ >
Im skalaren Kontext liefert localtime() das Ergebnis des Systemaufrufes
ctime zurück. Im Listenkontext liefert localtime()
ein Array, dessen Felder mit mit den Elementen der aktuellen Uhrzeit in der lokalen Zeitzone
initialisiert sind:
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
Nicht immer wird der Datentyp des gewünschten Ergebnisses aus dem Kontext klar, z.B:
print localtime();
Um in solchen Fällen klare Verhältnisse zu schaffen bietet PERL die
Möglichkeit durch Voranstellen der Schlüsselworte
scalar oder array den Typ des Rückgabewertes zu
beeinflussen. Wenn der Typ des Rückgabewertes nicht explizit oder implizit
definiert ist, dann wird ein Skalar zurückgeliefert.
PERL bietet die üblichen Schleifenkonstrukte, also for,while,until Schleifen, sowie (als foreach-Schleife) eine Listeniteration, ähnlich der FOR-Schleife aus der Shellprogrammierung.
Die einfachste Schleife is die foreach-Schleife, die einen Anweisungsblock
für alle Elemente einer Liste ausführt. Das Aktuelle Listenelement wird
über die Variable "$_" angesprochen:
@list = (1,2,3,4,5,6,7,"letztes Element");
foreach (@list){
print "$_";
print "\n";
}
Zum Abschluss dieses Abschnittes darf nicht verschwiegen werden, dass das Schluesselwort
foreach eigentlich überflüssig ist: Alle Beispiele in diesem Abschnitt
erzeugen funktionsgleichen Code, wenn man das Schlüsselwort "foreach" durch
ein schlichtes "for" ersetzt.
Im Folgenden werden wir trotzdem aus Bequemlichkeitsgründen einen gedanklichen Unterschied zwischen "for-Schleife" und und "foreach-Schleife" machen, indem wir mit dem Begriff "for-Schleife" eine Schleife mit Zählvariable, mit dem Begriff "foreach-Schleife" eine Schleife beschreiben, die über die Elemente einer Liste iteriert.
Die for-Schleife ist eine Zählschleife die in Syntax und Funktion weitgehend der for-Schleife in "C" entspricht. Das Script:
#!/usr/bin/perl -w
#
for ($i=0; $i<100; $i++ ){
print "\n $i";
}
schreibt zeilenweise alle Zahlen von 0 bis 100 auf die Konsole. Die for-Schleife besteht aus den folgenden Komponenten:
for ( < Definition > ; < Kriterium > ; < Manipulation > )
{
.....
}
Eingeleitet wird die for-Schleife durch das Schlüsselwort for. dann folgen drei durch Semikolons getrennte Ausdrücke innerhalb von eckigen Klammern:
Abgeschlossen wird die for-Schliefe durch den in geschweiften Klammer eingeschlossenen Anweisungsblock.
Wie in "C" kann eine for-Schleife mehrere Schleifenvariable bearbeiten. Die Ausdrücke für Definition, Kriterium und Manipulation können jeder für sich aus mehreren unabhängigen Ausdrücken bestehen, die dann durch Kommata ',' getrennt werden. Das Script fibonacci.pl zeigt eine solche Konstruktion:
#!/usr/bin/perl -w
#
for( $new=2, $old=2, $aux=0 ;
1 ;
$aux=$new , $new=2*$old+$new , $old=$aux, print "$old \n"){}
Das Script gibt die Fibonaccizahlen mit der rekursiven Definition f(n+2)= 2*f(n)+f(n+1) und Startwerten f(1)=2,f(2)=2 aus. Diese Zahlen beschreiben das Vermehrungsverhalten von unsterblichen Monsterkaninchen.
Die while-- und until Schleife in PERL sind besonders einfach strukturiert. Sie bestehen aus dem jeweiligen Schlüsselwort (while oder until), einem in runden Klammern eingeschlossenen Ausdruck der zu einem Wahrheitswert evaluiert werden kann und einem in geschweiften Klammern eingeschlossenen Anweisungsblock:
$i=0;
while ( $i <= 10 ) {
$i++;
print "$i \n";
}
until ( $i <= 0 ) {
$i--;
print "$i \n";
}
Der Anweisungsblock der while-Schleife wird ausgeführt solange die Testbedingung erfüllt ist, (d.h. Der Test wird vor der eventuellen Ausführung des Blockes durchgeführt), der Anweisungsblock der until-Schleife wird ausgeführt bis die Testbedingung erfüllt ist, (d.h. Der Test wird erst nach der Ausführung des Blockes durgeführt). Im obige Codebeispiel durchläuft $i also die Zahlen von 0 bis 10 vorwärts, und von 10 bis 0 rückwärts.
Im vorangegangenen Abschnitt hatten wir sehr intuitiv mit dem Begriff des "Wahrheitswertes" gearbeitet. Da PERL keine boolschen Type kennt stellt sich natürlich die Frage, was ein solcher "Wahrheitswert" denn nun wirklich ist. Die Antwort darauf kann erstaunlich kurz ausfallen:
FALSCH ist alles was in einem skalaren Kontext zu zum leeren String ("") oder zu "0" evaluert. Alles andere is "WAHR"
Um es an einigen Beispielen zu illustrieren: Wahr sind:
Falsch sind hingegen:
Dank des "execute" features von PERL lassen sich diese Fälle leicht direkt an der Kommandozeile verifizieren:
perl -e ' while( 1) {print "forever" } '
perl -e ' while("nichtleerer String") {print "forever" } '
perl -e ' while(0) {print "never" } '
perl -e ' while($undefinierter_SCALAR) {print "never" } '
perl -e ' while(undef) {print "never" } '
Boolsche Ausdrücke (d.h: Ausdrücke die zu Wahrheitswerten evaluieren) lassen sich auch klammern, verneinen, verunden verodern... usw. Dies wird in dem Abschnitt über Abschnitt über Programmverzweigungen noch ausführlicher beschrieben.
Neben der foreach-Schleife gibt es auch noch die Konstrukte map und grep um einen Ausdruck oder einen Anweisungsblock iterativ über Elemente einer Liste auszuführen.
Die Konstrukte:
grep { ...Block... } @liste;
grep Expression, @liste
evaluieren den Anweisungsblock in geschweiften Klammern bzw. den Ausdruck "Expression" .
Falls Block respektive Expression einen wahren Wert zurückliefern, wird das entsprechende
Listenelement in die Ergebnisliste eingereiht.
Die "map"-Konstrukte
map { ...Block... } @liste;
map Expression, @liste
evaluieren den Block bzw. den Ausdruck
für alle Elemente der Liste, und liefern dann eine Liste aller Rückgabewerte zurück.
Das Script "mapUndGrep":
#!/usr/bin/perl
#
@list=(1,2,3,4,5,6,7);
#
print (grep { eval ( $_ < 5 ) } @list ); print "\n" ;
print (grep $_ < 5 , @list ); print "\n" ;
print (map { eval ( $_ + 5 ) } @list ); print "\n" ;
print (map $_ + 5 , @list ); print "\n" ;
Liefert entsprechend die Ausgabe:
1234
1234
6789101112
6789101112
Das Wort Hashtable bedeutet eigentlich Wühltisch oder Grabbeltisch .
Hashtables (kurz Hashes) oder assoziative Arrays sind spezielle Arrays, bei denen die Adressierung der Elemente nicht über Indexnummern, sondern über beliebige Zeichenketten erfolgt. Ein bezeichnender Unterschied zwischen PERL und den meisten anderen objektorientierten Programmiersprachen ist die Tatsache, dass Hashtables in PERL eingebaute Datentypen sind, während sie in den meisten anderen OO-Sprachen durch Objekte realisiert werden die von externen Bibliotheken (in JAVA: java.util.Hashtable, in C++: STL o.ä. ) bereitgestellt werden. PERL benutzt Hashtables um Objekte zu realiseren und die C-Konstrukte "struct" und "union" bzw das PASCAL-Konstruct "record" zu ersetzen.
Eine Zeichenkette über die ein Element eines Hashtables angesprochen (indiziert) wird heißt "key" (Schlüssel), der inidzierte Wert selber "value" (Wert). Hashes werden bei der Deklaration durch ein vorangesteltes Prozentzeichen '%' ausgezeichnet.
Arrays mit einer geraden Anzahl von Elementen definieren in natürlicher Weise Hashtables dadurch, dass die Listenelemente mit geradem Index die Keys, und die jeweils unmittelbar darauf folgenden Listenelemente mit ungeradem Index die Values definieren. Auf diese Weise lassen sich Hashes sehr einfach anlegen:
%billhash=( "vorname" , "Bill", "Nachname" , "Kates", "Firma", "Winzigweich" );
Für Ästheten bietet Perl die Möglichkeit, die Kommata zwischen Key und Value
durch den Operator "=>" zu ersetzen:
%billhash=( "Vorname" => "Bill", "Nachname" => "Kates", "Firma" => "Winzigweich" );
Schließlich gibt es auch noch die Möglichkeit einzelnen Keys Values zuzuweisen,
und dem Perl Compiler die Arbeit der Deklaration des Hashes zu überlassen.
Dazu wird die Syntax $hashName{<key>}=<value> verwendet:
$billhash{"Vorname"} = "Bill";
$billhash{"Nachname"} = "Kates";
$billhash{"Firma"} = "Winzigweich";
Das Ergebnis ist in allen Fällen der gleiche Record, der einige Daten von Bill enthält:
----------------------------
| KEY | VALUE |
|----------|-----------------|
|'Vorname' | 'Bill' |
|'Nachname'| 'Kates' |
|'Firma' | 'winzigweich' |
----------------------------
Um auf einzelne Elemente (Values) des Records zuzugreifen wird das Konstrukt:
$<hashName>{<key>}; verwendet:
print "\n", $billHash{"Vorname"},"\n"
print "\n", $billHash{"Name"},"\n"
print "\n", $billHash{"Firma"},"\n"
Einzelne Felder des Hashes können mit der Funktion delete
gelöscht werden.(delete($hashname{key})) :
delete($billHash{"Vorname"});
delete($billHash{"Name"});
delete($billHash{"Firma"});
Die Funktion exists :
( exists(%hashname{key}))
prüft ob zu einem gegebenen Key ein Value existiert:
if ( exists($billHash('Vorname'))){
print $billHash('Vorname');
}
(Wobei die obige if-Anweisung einen dreisten Vorgriff auf das Kapitel über Programmverzweigung darstellt)
Die Funktion keys liefert eine Liste aller Schlüssel:
(keys($hashname)):
foreach keys( %billHash){
print $billHash($_);
}
Außerdem gibt es noch die Funktion each, die sich durch den Hash durcharbeitet, und bei jedem Aufruf einen zweielemtigen Array, besthend aus key und value zurückliefert. Nachdem der Hash abgearbeitet ist liefert each NULL zurück.
while ( ($key,$value) = each(%billHash)){
print "\n $key -> $value \n ";
}
Ausser Skalaren, Listen und Hashtables stellt PERL noch einen etwas ausgefallenen eingebauten Datentyp zur Verfügung, den "Typeglob". Ein Typeglob ist ein Verweis auf alle Objekte eines Namens innerhalb einer "Symboltabelle". Wir werden den Typeglob daher erst zu einem späteren Zeitpunkt zusammen mit den Symboltabellen besprechen.
Für eine Programmverzweigung nach zwei oder mehreren Alternativen unterstützt PERL ein if/elsif/else Konstrukt, dessen Syntax des if/else Konstruktes unter "C" nachempfunden ist. Das if Konstrukt kann folgende Komponenten enthalten:
das Schlüsselwort "if" gefolgt von einem Ausdruck der zu einem Wahrheitswert (true,false) evaluiert werden kann, gefolgt von einem Anweisungsblock, der in geschweiften Klammern eingeschlossen ist. (Anders als bei "C" und verwandten Sprachen können die geschweiften Klammmern nicht weggelassen werden !)
das Schlüsselwort "elsif"
gefolgt von einem Ausdruck der zu einem Wahrheitswert (true,false) evaluiert werden kann,
gefolgt von einem Anweisungsblock, der in geschweiften Klammern eingeschlossen ist.
Ein if/elsif/else Konstrukt kann beliebig viele dieser elsif-Blöcke enthalten.
Wenn die nach dem einleitenden "if" formulierte Bedingung nicht erfüllt ist,
werden die folgenden elsif Blöcke der Reihe nach untersucht.
Sobald die eine Bedingung erüllt ist, verzeigt das Programm in den folgenden Block,
eventuell weiter folgende elsif oder else Blöcke werden nicht mehr erreicht.
Man beachte auch die eigenwillige Schreibweise: "elsif", nicht elseif
Die elsif Konstruktion erlaubt multiple Programmverzweigungen, ähnlich
wie die case Konstrukte anderer Sprachen.
Ein if/elsif/else Konstrukt kann beliebig viele "elsif" Blöcke enthalten.
Wenn keine der durch if oder elsif
eigeleiteten Bedingungen erfült ist, wird eine der Anweisungsblock hinter
dem schlüsselwort else angesprungen.
Das Script "if.pl" demonstriert die Verwendung aller drei Konstrukte:
Mit der Funktion time() wird die Anzahl der seit dem 1.1.1970 verstrichenen
Sekunden abgefragt. Anschließend wird geprüft ob, diese Zahl beim Teilen durch 4 einen
Rest von 1,2,3 oder keinen Rest ergibt:
#!/usr/bin/perl
#
#
$now=time();
if ($now % 4 == 1)
{
print "\n\n";
print "Rest 1 \n";
print "$now \n\n";
}
elsif ( $now % 4 == 2)
{
print "\n\n";
print "Rest 2 \n";
print "$now \n\n";
}
elsif ($now % 4 == 3)
{
print "\n\n";
print "Rest 3 \n";
print "$now \n\n";
}
else
{
print "\n\n";
print "durch 4 teilbare Zahl \n";
print "$now \n\n";
}
Logische Ausdrücke können (und sollten) mit runden Klammern "()" geklammert werden.
Zur Verknüpfung von logischen Ausdrücken untereinander existieren die Operatoren:
!" logisches NOT &&" logisches UND ||" logisches ODER not" logisches NOT and" logisches UND or" logisches ODER xor" logisches XOR Die letzten vier Operatoren besitzen die gleiche Funktionalität wie die entprechenden
Operatoren !,&& und ||, aber eine andere Priorität.
Die folgenden Operatoren können zum Vergleich von Zeichenketten und/oder Zahlen benutzt werden:
==" true bei Gleichheit !=" true bei Ungleichheit >" true, falls linke Seite
größer als rechte Seite <" true, falls linke Seite
kleiner als rechte Seite >=" true, falls linke Seite
größer oder gleich rechter Seite <=" liefert true, falls linke Seite
kleiner oder gleich rechter Seite <=>" liefert -1,0,1 je nachdem, ob die linke Seite
kleiner,gleich oder größer als
die rechte Seite ist
eq" true bei Gleichheit ne" true bei Ungleichheit gt" true, falls linke Seite
größer als rechte Seite lt" true, falls linke Seite
kleiner als rechte Seite ge" true, falls linke Seite
größer oder gleich rechter Seite le" true, falls linke Seite
kleiner oder gleich rechter Seite cmp" -1,0,1 je nachdem, ob die linke Seite
kleiner,gleich oder größer als
die rechte Seite ist
(sogenannter Spaceship Operator )Außerdem wird auch der Match Operator "=~"
häufig benutzt. Damit wird getestet ob eine Zeichenkette ein
bestimmtes Muster enthält. Die Verwendung des Matchoperators
wird im
Abschnitt über reguläre Ausdrücke
noch ausführlich besprochen.
Die Verwendung von ungeeigneten Vergleichsoperatoren führt oft zu "unintuitiven" Ergebnissen. Der Codeschnipsel:
if ("walter"=="heinz") { print "walter == heinz "; }
liefert die Ausgabe: walter==heinz,
weil hier der == Gleichheitsoperator zum Vergleich von Zeichenketten missbraucht wurde.
Rechte und linke Seite des "==" Operators werden zum numerischen Wert "0" evaluiert,
und dann erst verglichen.
Syntaktische Varianten zum if/elsif/else Konstrukt lassen sich mit unless und dem Operator ?: realisieren:
Die Sequenz
unless (expression) { Block }
ist äquivalent zu
if (! (expression)) { Block }
d.h: der Block wird ausgeführt, wenn der Ausdruck (expression) false zurückliefert.
Die "?:" Variante funktioniert wie in C:
expression ? Anweisung1 : Anweisung2;
Wenn der Ausdruck expression "true" zurückliefert,
wird die Anweisung1 ausgeführt, sonst Anweisung2.
Arrays sind " Listen von Skalaren ". Wie aber realisiert man in PERL Konstrukte wie Matritzen, also zweidimensionale Arrays bzw. Arrays deren Listeneinträge wieder Arrays sind?
Die einfachste Lösung für dieses Problem
(und eigentlich für die Erzeugung von komplexen Datenstrukturen generell)
ist es sogenannte " Referenzen " zu verwenden.
Eine Referenz ist ein Skalar, der auf eine andere Datenstruktur verweist.
Mit dem Referenzierungsoperator " \" wird aus einer Datenstruktur eine
Referenz erzeugt und zurückgegeben:
$myscalar="ein scalar";
@myarray=("ein","array");
$reference1=\$myscalar;
$reference2=\@myarray;
print "\n $reference1 \n";
print "\n $reference2 \n";
Hier werden mit den Zeilen
$reference1=\$myscalar;
$reference2=\@myarray;
Referenzen auf den Skalar bzw das Array erzeugt. Die Ausgabe des Codesnippets zeigt , das es sich bei den Referenzen nicht um gewöhnliche Skalare oder Arrays handelt:
SCALAR(0x80eddec)
ARRAY(0x80edea0)
Will man anstatt mit den Referenzen wieder mit den ursprünglichen Datenstrukturen arbeiten, dann müssen die Referenzen zuerst dereferenziert werden. Dies geschieht durch das Voranstellen der Zeichen "$","@" oder "%" je nachdem ob die Referenz in einen Skalar, ein Array oder einen Hash umgewandelt werden soll:
$myscalar="ein scalar";
@myarray=("ein","array");
$reference1=\$myscalar;
$reference2=\@myarray;
print "\n $$reference1 \n";
print "\n @$reference2 \n";
Erledigt diese Aufgabe korrekt, und liefert die Ausgabe :
ein scalar
ein array
Aber Vorsicht:
Um die Dereferenzierung korrekt zu erledigen muss der Datentyp des zu dereferenzerenden Objektes vorher bekannt sein.
PERL bietet zu diesem Zweck die Funktion "ref", die Auskunft darüber
gibt welche Art von Datenstruktur sich hinter der Referenz versteckt:
Rückgabe nch Aufruf von ref ($scalar) :
Skalar (keine Referenz)
Referenz auf Array
Referenz auf Hashtable
Referenz auf SCALAR
Referenz auf Funktion
Referenz auf Referenz
Die folgenden Scripts demonstrieren noch einmal das Erzeugen und Dereferenzieren von Referenzen auf Verschiedene Datentypen:
Skalare:
#!/usr/bin/perl
$scalar="Wert";
$scalarref=\$scalar;
print "die Referenz (Adresse) : ", $scalaref ," \n\n";
print "der (dereferenzierte) Wert : $$scalarref \n\n";
Arrays:
#!/usr/bin/perl
@array = ( 'AAA', 'BBB', 'CCC' );
$arrayref = \@array;
print "der (dereferenzierte) Wert : @$arrayref \n\n";
Hashes:
#!/usr/bin/perl
%hash = ('1' => 'AAA','2' => 'BBB', '3' => 'CCC' );
$hashref = \%hash;
%derefHash=%$hashref;
foreach $key (keys(%derefHash)) # foreach
{ print "\n \n";
print "$key -> ";
print ($derefHash{"$key"});
}
print "\n\n";
Anders als Zeiger in "C" enthalten Referenzen keine echten Speicheradressen, die Zuordnung von der Referenz auf die Daten wird im Hintergrund vom PERL-Runtime Environment erledigt. Dieses Verfahren hat den Vorteil, dass die berüchtigten dangling Pointers , also Zeiger, die auf nicht mehr existierende Datenstrukturen verweisen, in PERL nicht existieren. Genausowenig braucht PERL Funktionen zum Allozieren und Freigeben von Speicherplatz. Das PERL Runtime Environment erzeugt die notwendigen Speicherstrukturen einfach bei Bedarf, und gibt den Speicher wieder frei, wenn er nicht mehr benötigt wird (i.e: Wenn keine Referenzen mehr auf das Objekt verweisen).
Wie schon bemerkt lassen sich Referenzen auch verwenden, um kompliziertere Datenstrukturen (Arrays von Arrays, Hashes von Hashes, usw..) zu realisieren. Im Script "matrix.pl" wird ein zweidimensionales Array erzeugt, indem ein Array definiert wird dessen Einträge Referenzen auf weitere Arrays sind:
#!/usr/bin/perl
#
#
@line1=(11,12,13);
@line2=(21,22,23);
@line3=(31,32,33);
@matrix=(\@line1,\@line2,\@line3);
for($i=0;$i<=2;$i++){
$ref=$matrix[$i];
@ar=@$ref;
print @ar,"\n";
}
Eine sparsame syntaktische Variante beim Erzeugen von Referenzen auf Arrays ist die Auflistung der Elemente in eckigen Klammern:
$ref=["Element 1","Element 2","Element 3"];
Dies wirkt ähnlich wie der schon bekannte Ausdruck
@array=("Element 1","Element 2","Element 3");
Während die zweite Variante das neu erzeugte Array zurückliefert, gibt die erste Variante eine Referenz auf das neu erzeugte Array zurück.
Zur Arbeit mit Ein und Ausgabeströmen bietet PERL den Datentyp "Filehandle". Filehandles bieten eine einheitliche Schnittstelle für die Kommunikation mit Dateien und Prozessen.
Um eine Datei im aktuellen Verzeichnis zum Lesen zu öffnen legt man mit der Anweisung
open (FILEHANDLE,"< Dateiname") ;
ein Filehandle an, über das von jetzt an lesend auf die Datei zugegriffen werden
kann. Filehandles für den schreibenden Zugriff auf Dateien erzeugt man mit den
Umlenkungszeichen ">" und ">>:";
open (FILEHANDLE,"> Dateiname");
open (FILEHANDLE,">> Dateiname");
Der Unterschied zwischen beiden Zugrifsmethoden > und >> liegt in der Art,
wie beim Schreiben in die Datei mit den bereits vorhandenen Daten umgangen wird. Bei der ersten
Variante (">") wird die
Datei mit den neuen Daten überschrieben ("overwrite"), und die alten Daten gehen verloren,
bei der zweiten Variante (">>") werden die neuen Daten an das Ende der Datei
angehängt ("append"), die alten Daten bleiben erhalten.
Wie in anderen Programmiersprachen wird der Lese/Schreibzugriff in der Regel gepuffert. (D.h: es werden bei jedem Lese/Schreibzugriff gleich eine größere Menge Daten als angefordert von der Platte gelesen, und im Speicher für weitere Operationen zur Verfügung gehalten). Ähnlich wird beim Schreiben vorgegangen: Die zu schreibenden Zeichen werden zuerst in einen Zwischenspeicher, und erst dann, in grösseren Portionen, auf die Platte geschrieben.
Durch die close-Anweisung wird der Zwischenspeicher engültig geleert,und die belegten Ressourcen wieder freigegeben. Obwohl dies eigentlich am Ende der Programmausführung durch das Runtime Environment automatisch erfolgen sollte ist es (wie in anderen Programmiersprachen auch) guter Stil, offene Filehandles möglichst früh zu schließen. Zum einen erfolgt keine Speicherbelegung durch ungenutzte Filehandles, zum anderen kann man sicher sein, dass bei einem anschließenden unsanften Programmabbruch (Steckerziehen...) tatsächlich alle Daten geschrieben wurden. Die Syntax der close-Anweisung ist ein einfaches
close <FILEHANDLE>;
Für den lesenden Zugriff auf Dateien spielt die Pufferung keine so bedeutende Rolle. Man kann Dateien öffnen, aus Dateien lesen und dann ohne vorheriges Schließen mit dem gerade aktiven Filehandle eine andere Datei öffnen. In diesem Fall wird der Dateizeiger des Filehandles aber nicht zurückgesetzt, sodass die neu geöffnete Datei i.A. nicht von vorne nach hinten gelesen wird. Dieser Effekt wird i.A nicht gewünscht sein.
In der Regel wird der weitere Programmverlauf davon abhängen, ob das Anlegen eines Dateihandles
erfolgreich verlaufen ist. "open" liefert im Erfolgsfall
ein "TRUE", bei Miserfolg ein "FALSE" zurück.
In Skripts findet man "open" oft in Kombination mit der "die"
Funktion, die das Script mit einer Fehlermeldung beendet. Der folgende Codeschnipsel
versucht die Datei "datei.txt" zu öffnen, und beendet die weitere
Programmausführung mit der Ausgabe einer Fehlermeldung und des aktuellen
SystemErrorString "$!", falls das Öffnen fehlschlägt:
open(DBASE, '< datei.txt') # "datei.txt" oeffnen
|| die "datei.txt kann nicht geoeffnet werden : $!";
Der gute Stil erfordert, dass jeder Aufruf von "open" von entsprechendem Code
zum Abfangen der Fehler begleitet wird. Wenn wir im Folgenden gelegentlich davon abweichen,
dann geschieht das aus didaktischen Gründen, um den Umfang der Beispiele möglichst
gering zu halten...nicht etwa aus Faulheit...aehem..ja.
Für den lesenden Zugriff stellt PERL den "<>"-Operator bereit.
Dieser arbeitet recht unterschiedlich, je nachdem ob er im skalaren oder im
Listenkontext aufgerufen wird.
Im Listenkontext liefert der Ausdruck <FILEHANDLE> ein Array, in dessen Feldern sich die
Zeilen der Eingabedatei befinden (readfileArray.pl):
#!/usr/bin/perl
#
# die Passworddatei als Array auslesen
# (<...> Operator im Listenkontext)
#
open (INFILE,"/etc/passwd");
@ar= (<INFILE>);
print join ("/n",@ar);
Im skalaren Kontext liefert jeder Aufruf von <FILEHANDLE> die aktuelle
Zeile der Datei und setzt (soweit möglich)die aktuelle Zeile eine Zeile
weiter.
Im folgenden Beispiel readfileScalar wird bei jedem Aufruf von
<INFILE> eine neue Zeile der Passworddatei zurückgeliefert, solange bis
keine Zeile mehr zurückgeliefert werden kann (undef).
#!/usr/bin/perl
#
# einen unendlichen Strom von Zeichen
# Zeilenweise einlesen
#
open (INFILE,"/etc/passwd");
while (<INFILE>){
$input=$_;
print("-> $input ");
}
Für die Unix-Kanäle
stdin (Standardeingabe),
stdout (Standardausgabe) und
stderr (Standard-Fehlerausgabe)
existieren in PERL die vordefinierten Filehandles
STDIN ,
STDOUT ,
STDERR
Der Aufruf des <> Operators ohne eingeschlossenes Filehandle liefert
(wie der Ausdruck <STDIN>) die aktuelle Eingabezeile.
Im Un*x Umfeld sind Pipelines (kurz Pipes ) ein beliebtes Werkzeug der
Shellprogrammierung um die Eingabe und Ausgabe verschiedener Programme miteinander zu
verknüpfen. So kann man zum Beispiel die Ausgabe des Kommandos "find /etc",
das eine Liste aller Dateien unterhalb des Verzeichnisses "/etc" erzeugt,
mit der Eingabe des Kommandos "grep pass" verbinden, das einen Eingabestrom
nach Zeilen durchsucht die die Zeichenkette "pass" enthalten:
find /etc | grep pass
Zur Umleitung dient das sogenannte "Pipezeichen" |, das Ergebnis ist
eine Liste aller Dateien, die die Zeichenkette "pass" enthalten.
Unter Betriebssystemen die einen brauchbaren Pipe-Mechanismus bereitstellen lässt sich die Interprozesskommunikation über pipes auch direkt aus Perl Programmen heraus verwenden. Dabei wird ein aus dem PERL-Programm heraus ein Prozess gestartet, in dessen Eingabekanal dass PERL Script Daten hineinschreibt, oder aus dessen Ausgabekanal das PERL Script Daten herausliest. Schreiben und Lesen erfolgen jeweils über ein Filehandle.
Um aus eine Pipe zu lesen wird ein Filehandle erzeugt, bei dem anstatt einem Dateinamen der Aufruf des Prozesses, gefolgt von einem Pipesign angegeben wird:
open ( FILEHANDLE ,"programmaufruf |")
Um in eine Pipe zu schreiben wird ein Filehandle erzeugt bei dem anstatt einem Dateinamen ein Pipesign gefolgt vom Aufruf des Prozesses angegeben wird:
open ( FILEHANDLE ,"| programmaufruf ")
Der folgende Code (searchPass.pl) tut zwar nichts weiter als die oben vorgestellte Shell-Pipe
find /etc | grep pass
Demonstriert aber das Öffnen und Schließen von Pipes mit PERL:
#!/usr/bin/perl
open (INPUT, "find /etc |");
@input=<INPUT>;
close(INPUT);
open (OUTPUT, "| grep pass ");
print OUTPUT @input ;
close(OUTPUT);