Perl - Grundlagen


Perl - Programm

Ein Programm in  Perl  (Practical Extraction and Report Language) wird per Text-Editor in eine ASCII-Datei geschrieben.Verbreitet - aber nicht zwingend - wird die Dateinamenserweiterung  .pl  verwendet.

Ein einfaches Programm zur Ausgabe einer Textzeile auf dem Bildschirm (bzw. an die Standard-Ausgabe) sieht folgendermaßen aus (unter  UNIX / AIX ) :
 
 

 #!/usr/bin/perl
 #
 # Ausgabe einer Textzeile

 print "Dieser Text wird ausgegeben\n";


 # Pfad des Perl - Interpreters

 # Kommentar (beg. mit #, bis Zeilenende 

 # print - Anweisung, mit Zeilenvorschub
 

Anweisungen werden mit  ;  abgeschlossen. Leerzeilen innerhalb des Programms sind erlaubt.



Dateibearbeitung


Dateien müssen in Perl für die Bearbeitung geöffnet werden:
 
 

open ( FH , "Dateiname")
open ( FH , ">Dateiname")

open ( FH , ">>Dateiname")

 # zum Lesen
 # zum Schreiben (bish. Inhalt
 # überschreiben)
 # zum Fort-Schreiben ("anhängen")

FH steht dabei für ein "File-Handle" (frei wählbar, Grossbuchstaben üblich), d.i. ein Datei-Bezeichner innerhalb des Perl-Scriptes.

Vordefiniert (und ohne expl. open geöffnet) sind die File-Handles
 

STDIN        -       Normaleingabe  (Tastatur)
STDOUT       -       Normalausgabe  (Bildschirm)
STDERR       -       Fehlerausgabe  (Bildschirm)
Einlesen:
$Zeile = <FH>;       # Variable $Zeile erhält Inhalt der nächsten
                     # Zeile (einschl. Zeilenwechsel).
@Zeilen = <FH>;      # alle - noch nicht gelesenen - Zeilen werden der Liste (array)
                     # @Zeilen als Werte zugewiesen.
Ausgeben:

     print FH Ausgabewerte;      # Ausgabewerte werden durch Komma getrennt angegeben
                                 # ein Zeilenwechsel ist expl. anzugeben mit "\n".

     Beispiel:

         open(AUS,">ausgabe.dat");
         print AUS "Hallo"," Welt","\n";    # 1 Zeile mit Inhalt: Hallo Welt
         print AUS "Hallo Welt\n";          # ebenso

Bei Nutzung der Standard-Ein-/Ausgabe braucht kein File-Handle angegeben zu werden:

          $z = <>;       bzw.     print  Ausgabewerte;

NB : Für das Testen von CGI-Scripts kann / sollte man die Fehlerausgabe in eine Datei lenken, z.B. durch:

     open(STDERR, ">error.dat");

In der Datei  error.dat  (im gleichen Verzeichnis wie das CGI-Script) werden dann Warnungen und Fehlermeldungen abgelegt.

Nach Ende der Bearbeitung wird die mit FH verbundene Datei geschlossen mit der Anweisung

     close(FH);

Sie kann dann im gleichen Programm auch wieder geöffnet werden, z.B. für eine "andere Bearbeitungsart" (etwa zunächst schreiben, später lesen).
Am Ende des Programmablaufes werden alle noch geöffneten Dateien automatisch geschlossen.




Vergleiche

 
  Vergleich   Zahlen   Strings
 gleich   ==   eq
 ungleich   !=   ne
 kleiner als   <   lt
 größer als   >   gt
 kleiner oder gleich   <=   le
 größer oder gleich   >=   ge

Die Verwendung unterschiedlicher Operatoren für Zahlen und Strings ist notwendig, da ja Variable keinen festgelegten Typ besitzen, sondern vielmehr ihr Inhalt gemäß dem Kontext interpretiert wird.

Beispiel:    $a = 38;
             $b = 7;
             $a > $b         #  wahr
             $a lt $b        #  wahr



Kontrollstrukturen

Kontrollstrukturen steuern in Perl den Ablauf des Programms anhand des "Wahrheitswertes" eines Ausdrucks.
Ein Ausdruck hat den Wert falsch, wenn sich nach Auswertung als Ergebnis ein leerer String, Null als String oder die Zahl Null ergibt, sonst wahr, z.B.

                   falsch           0   1-1   ""   "0"

                   wahr            1    "1"   "00"   "0.000"

Zusätzlich können logische Operatoren zur Verknüpfung bzw. Verneinung eingesetzt werden:

     $a && $b      # sind $a und $b wahr ?  Genauer: $a, falls $a falsch, sonst $b

     $a || $b      # ist $a oder $b wahr ? Genauer: $a, falls $a wahr, sonst $b

     !($a)         # ist $a falsch ?

Die Auswertung einer Verknüpfung beginnt links und bricht ab, sobald eine Entscheidung gefallen ist, d.h. das Ergebnis ist der Wert von $a, wenn dieser bereits das Ergebnis unabhängig von $b festlegt ($b wird dann gar nicht ausgewertet).

Mittels logischer Verknüpfungen können an Stelle $a , $b auch Kommandos "abhängig voneinander" ausgeführt werden. Wahr bzw. falsch resultiert dabei aus der (nicht) erfolgreichen Ausführung (Return-Werte 1 bzw. 0):

     cmd1 && cmd2;  # cmd2 wird nur ausgeführt, falls cmd1 erfolgreich war
     cmd1 || cmd2;  # scheitert cmd1, wird cmd2 ausgeführt

Beispiel:

          open (FH, "Datname") || die "kann Datei nicht öffnen\n";




Schleifen

 
foreach  $var  (@liste)  {

         print  "$var\n";

}

# Für jedes Element der Liste werden
# die folg. Anweisungen ausgeführt
# (Veränderungen von $var wirken
#  auf die Elemente von @liste)

Der Anweisungsblock wird auch für undefinierte Elemente der Liste durchlaufen. Bei leerer Liste geschieht nichts.



 
 
while  (ausdruck)  {

        Anweisung(en)

}

# Ausführung der Anweisungen erfolgt
# so lange, wie die Auswertung von
# ausdruck wahr ergibt.

Es muss gewähleistet sein, dass ausdruck durch die Schleifen-Durchläufe "irgendwann" den Wert falsch erhält.
Ist ausdruck "gleich zu Beginn falsch", erfolgt gar keine Ausführung.

Beispiele:
           while  ($z = <>)    # Ausführung für jede Zeile der Standard-Eingabe
           while  ($i < 100)   # Ausführung, solange $i kleiner als 100 ist

Benutzt man until an Stelle von while, erfolgt die Ausführung so lange, bis ausdruck den Wert wahr erhält (keine Ausführung, wenn "gleich zu Beginn wahr").

Mit Hilfe des do - Operators kann man den while- oder until-Test an das Ende des Anweisungsblocks verlagern :

          do  {
                Anweisung(en)
          }  while  (ausdruck);

Man erzielt damit mind. eine Ausführung der Anweisung(en), weitere folgen, falls Ausdruck wahr (while) bzw. falsch (until) liefert.



 
 
for  (Anf-Wert; Bed; Inkrement)  {

       Anweisung(en)

}

# Anfangswert wird gesetzt, dann,
# falls Bed wahr, wird Anweisung(en)
# ausgeführt, danach Inkrement.
# Falls Bed wahr, erfolgt weiterer
# "Durchlauf" usw.

Beispiel:

     for  ($i = 1; $i < 50.5; ++$i)  {     # Schleife gibt die Zahlen 1 - 50 aus
                  print "$i\n";
          }

Anm.: Die reservierten Worte for  und foreach können für beide Strukturen eingesetzt werden (Perl weiß dann schon...).

for / foreach können auch "explizit" Listen oder Bereiche übergeben werden, um deren Elemente nacheinander abzuarbeiten :

     for $i ((1,2,3,4,5)) { print "$i\n" } # dopp.Klammern, innere zur Listen-Def.
     foreach (1..5) { print "$_\n" }       # "ohne Variable" enthält $_ den
                                           # aktuellen Listenwert

Beide Schleifen geben die Zahlen von 1 bis 5 aus.



 

Bedingte Verzweigung

 
if  (ausdruck)  {
       Anweisung(en)1
else  {
       Anweisung(en)2
}
# ist ausdruck wahr ?

# falls ausdruck falsch

 

Es werden entweder  Anweisung(en)1  (ausdruck wahr)  oder  Anweisung(en)2  (ausdruck falsch) ausgeführt.
else mit folgendem Anweisungsblock kann entfallen, wenn nicht benötigt ("asymmetrisches if"). Andererseits können auch mehr Alternativen in die Verzweigung aufgenommen werden :

     if  (ausdruck1)  {             # falls ausdruck1 wahr
            Anweisung(en)1
     } elsif  (ausdruck2)  {        # falls ausdruck1 falsch und ausdruck2 wahr
                 Anweisung(en)2
     } else  {                      # falls ausdruck1 falsch und ausdruck2 falsch
                 Anweisung(en)3
     }

Falls man "nur die else-Alternative" benötigt, kann man statt if die unless-Anweisung verwenden :

      unless (ausdruck)  {                 # entspricht  if (!(ausdruck))
             Anweisung(en)
      }

Bestehen die Alternativen unter  if - else aus jew. einer Anweisung / Zuweisung kann man die Verzweigung mit dem "Konditional-Operator" ? :  schreiben, z.B.

      $a eq $b ? print "gleich" : print "ungleich";  # :  trennt die Alternativen
      print $a eq $b ? "gleich" : "ungleich";        # Wirkung wie vorher
      $b > $c ? $a = $b : $a = $c;                   # $a erhält den größeren der
      $a = $b > $c ? $b : $c;                        # Werte von $b, $c

NB.: Diese Schreibweise trägt nicht unbedingt zur Lesbarkeit eines Programms bei.





 

DBM - Datenbanken und Hashes

Unter UNIX-Systemen stellt eine Standardbibliothek DBM eine einfache Datenbankstruktur zur Verfügung, die wahlfreien Zugriff über Schlüsselwerte auf die Datensätze erlaubt. Benutzt werden dabei zwei Dateien dbname.pag  und  dbname.dir  als Daten- bzw. Schlüssel-Datei (dbname ist dabei frei wählbar, die Erweiterung  .pag  und  .dir  sind festgelegt).

Perl bietet die Möglichkeit, ein Hash mit einer DBM-Datenbank zu verknüpfen und dann die Bearbeitung der Datenbank "über dieses Hash" (auch DBM-Array genannt) abzuwickeln:

   dbmopen (%hashname, "dbname", modus);

"öffnet" die Datenbank  dbname  (bzw. legt sie, falls nicht existent, mit Zugriffsrecht modus an, z.B. 0644) und "verbindet" sie mit dem assoziativen Array  %hashname .

   dbmclose (%hashname);          # "schließt" die Datenbank

   $hashname {$schl} = ausdruck;  # setzt Datensatz zu $schl auf Wert v. ausdruck
                                  # ("überschreibt" bzw. "fügt hinzu")

   delete $hashname {$schl};      # löscht Datensatz zum Index $schl

   keys %hashname                 # Liste aller Schlüssel

   values %hashname               # Liste aller Werte (=Datensatzinhalte)
 

Beispiel: Das folgende Programm gibt alle Sätze einer DBM-Datenbank in eine sequentielle Datei aus, aufgerufen mit

     db2seq  dbname > seqdateiname          , falls Progamm in Datei db2seq steht.

   #!/usr/bin/perl
   #
   # Ausgabe einer DBM-Datenbank in seq. Datei
   # (DBM-Name als Aufrufparam., Std-Ausgabe
   #
   $file = shift;                                # shift wirkt hier auf @ARGV,
                                                 # liefert ersten Aufruf-
                                                 # Parameter, also dbname
   die "DB exist. nicht" unless -f "$file.dir";  # Abbruch, falls DB nicht
                                                 # existiert (geprüft
                                                 # anhand "Index-Datei")
   dbmopen (%db, $file, undef);                  # verbindet DB mit Hash  %db
                                                 # (undef: DB wird nicht neu
                                                 # angelegt, falls nicht vorh.)
   while (($schl, $wert) = each %db ) {          # alle "Hash-Element-Paare" und
                                                 # damit alle DB-Datensätze
                                                 # werden sukzessive durchlaufen
        print "$schl:$wert\n";                   # Ausgabeform Schlüssel:Datensatz
   }
   dbmclose (%db);

Die Umkehrung, d.h. Aufbau oder ggf. Erweiterung einer DBM-Datenbank aus einer sequentiellen Datei leistet:

Aufruf:   seq2db  dbname < seqdateiname     , falls Progamm in Datei seq2db steht.

   #!/usr/bin/perl
   #
   # Aufbau (Erweiterung) einer DBM-Datenbank aus seq. Datei
   # (DBM-Name als Aufrufparam., Std-Eingabe
   #
   $file = shift;                         # shift wirkt hier auf @ARGV, liefert
                                          # 1. Aufruf-Parameter, also dbname
   dbmopen (%db, $file, 0644);            # verbindet DB mit Hash  %db
                                          # (0644 : DB wird ggf. mit entspr.
                                          # Zugriffsrechten neu angelegt)
   while (<>) {                           # solange die Eingabe Sätze liefert
       ($schl, $wert) = m/^(.*?):(.*)$/;  # Satz wird am ersten: in Schlüssel und
                                          # Wert aufgeteilt
       $db{$schl} = $wert;                # Datensatz für Schlüssel $schl wird mit
                                          # Inhalt $wert angelegt / überschrieben
   }
   dbmclose (%db);


Ein vergleichbares "besseres" Datenbank-System bietet Berkeley DB.
Den beiden obigen Beispielen entsprechende Programme sehen so aus:

#!/usr/bin/perl
#
# Programm zum Einbringen einer seq Datei (Std-Eingabe)
#      in eine BerkeleyDB-Datei (Aufruf-Parm)
#

$file = shift;

die "give db name" unless $file;

#use strict;
use DB_File;

tie %dbhash, 'DB_File', $file
	or die "+++ Fehler bei Initialisierung der DB  $file\n";

while(<>) {
    ($key, $value) = m/^(.*?):(.*)$/;
    $dbhash{$key} = $value;
}

untie %dbhash;


#!/usr/bin/perl
#
# Programm zur Ausgabe einer BerkeleyDB-Datei (Aufruf-Parm)
#      in eine seq. Datei (Std-Ausgabe)
#

$file = shift;

die "give db name" unless -f "$file";

#use strict;
use DB_File;

tie %dbhash, 'DB_File', $file
	or die "+++ Fehler bei Anbindung der DB  $file\n";

while(($key, $value) = each %dbhash) {
	print "$key:$value\n";
}

untie %dbhash;