Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Konvertieren, int ceil sprintf hex oct floor Runden AbschneidenPerl Kurs Skripting Hash Hashes DBM DBM-Files grep sort undef delete keys values each DB Hash Hashes DBM DBM-Files grep sort undef delete keys values each DB Perl Kurs Kapitel 9

9.1. Hashes           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Hashes auch assoziatives Array genannt, kann man z.B. mit freidefinierbare Zeichenketten auf einzelnen Array-Elemente zugreifen. So sieht der  Syntax von Hashes aus ...

%HASH = (SCHLÜSSEL => WERT);

Hier erkennen Sie, dass Hashes mit dem Präfix % beginnen und mit einem Schlüsselnamen, womit Sie anschließend auf die einzelnen Elemente zugreifen können.

Folgende Schreibweisen könnten Sie für Hashes verwenden ...

%wochentag = ("Montag" , 1 , "Dienstag" , 2 , "Mittwoch" , 3, "Donnerstag" , 4 ,
 "Freitag" , 5 , "Samstag" , 6 , "Sonntag" , 7)

Aufgrund der besseren Lesbarkeit, empfiehlt es sich aber die Schreibweise mit den Zeichen => anstelle des Kommas zu verwenden. Bei dieser Schreibweise kann man auch auf das Quoting der Schlüssel verzichten ...

%wochentag = (Montag => 1, Dienstag => 2, Mittwoch => 3, Donnerstag => 4,
               Freitag => 5, Samstag => 6, Sonntag => 7);

Das mit dem Quoting gilt aber nur für die Schlüssel und nicht für deren Werte. Das ganze in der Praxis ...

#!/usr/bin/perl -w

%auto = ("Marke" => "BMW", "Baujahr" => 1999, "PS" => 113, "KFZ" => "A - BC111");

print $auto{Marke}, "\n";
print $auto{Baujahr}, "\n";
print $auto{PS}, "\n";
print $auto{KFZ}, "\n";

Sie sehen in unserem Programm oben, wie mit Hilfe von $hash{schlüssel}, auf die einzelnen Elemente zugegriffen wird..

WICHTIG: Der Schlüssel eines HASH muss eine Zeichenkette sein!!!

Nun ist diese Art (print) auf die einzelnen Elemente eines Hashes zuzugreifen, recht umständlich. Auch hier liefert uns Perl wieder eine Funktion, mit der wir die einzelnen Felder des Hashes durchzulaufen können. Die Funktion lautet keys. Mit dieser Funktion bekommen wir die Schlüssel in Form eines Arrays zurück. Das ganze auf unser Programm bezogen ...

#!/usr/bin/perl -w

use Tie::IxHash;  #Damit die richtige Reihenfolge ausgeben wird
                  #Modul muss eventuell nachinstalliert werden

%auto = ("Marke" => "BMW", "Baujahr" => 1999, "PS" => 113, "KFZ" => "A - BC111")

foreach (keys %auto)
 {
   print $_ . " : " . $auto{$_} . "\n";
 }

Hiermit erzeugen Sie in der foreach-Schleife den Schlüssel eines Hash. Dieser steht nun in der Standardvariablen $_. Welcher Schlüssel das ist, zeigt uns die folgende print-Ausgabe. Den Wert diese Schlüssels geben wir anschließend mit $auto{$_} aus.

Sollte Sie der Schlüssel nicht interessieren, sondern nur die Werte der Liste, können Sie dies mit dem Schlüsselwort values folgendermaßen realisieren ...

#!/usr/bin/perl -w

%auto = ("Marke" => "BMW", "Baujahr" => 1999, "PS" => 113, "KFZ" => "A - BC111")

foreach $wert (values %auto)
 {
   print $wert , "\n";
 }

Somit werden nur die Werte der Liste ausgegeben. Nun haben Sie in Perl auch die Möglichkeit Schlüssel/Wert-Paare hinzuzufügen und zu Entfernen. Löschen Sie  in unserer Liste das Schlüssel/Wert-Paar "PS" => 113  und fügen Sie das Schlüssel/Wert-Paar  "KWH" => 66 dafür hinzu ...

#!/usr/bin/perl -w

use Tie::IxHash;       #Damit die richtige Reihenfolge ausgeben wird
                       #Modul muss eventuell nachinstalliert werden oder auskommentieren
%auto = ("Marke" => "BMW", "Baujahr" => 1999, "PS" => 113, "KFZ" => "A - BC111");

foreach (keys %auto)
 {
   print $_ . " : " . $auto{$_} . "\n";
 }

print "\n";

delete $auto{PS};      #Wir entfernen das Schlüssel/Werte-Paar PS : 113

$auto{KWH} = 66;       #Wir fügen ein Schlüssel/Wert-Paar (KWH/66) hinzu

foreach (keys %auto)
 {
   print $_ . " : " . $auto{$_} . "\n";
 }

Um ein Schlüssel/Wert - Paar zu löschen, verwenden Sie einfach delete und den Schlüssel den Sie entfernen wollen. Und um ein neues Paar hinzu zu fügen, was noch einfacher ist, schreiben Sie einfach einen neuen Schlüssel vom Hashtyp %auto.

Das Hinzufügen eines neuen Wertes im Hash lässt sich noch einfacher realisieren ...

#!/usr/bin/perl -w

%auto = (Marke => "BMW", Baujahr => 1999, PS => 113, KFZ => "A - BC111");

foreach (keys %auto)
   {
     print $_ . " : " . $auto{$_} . "\n";
   }

#Wir fügen neuen Schlüssel mit Wert ein
$auto{'Preis'} = '25000 DM';
print "\n\n";

foreach (keys %auto)
   {
     print $_ . " : " . $auto{$_} . "\n";
   }

Wenn Sie nur den Wert eines Schlüssels löschen wollen, können Sie dies mit dem Schlüsselwort undef machen ...

#!/usr/bin/perl -w

%auto = (Marke => "BMW", Baujahr => 1999, PS => 113, KFZ => "A - BC111");

foreach (keys %auto)
   {
     print $_ . " : " . $auto{$_} . "\n";
   }

#Wir entfernen den Wert von KFZ.....
undef $auto{KFZ};
print "\n\n";

foreach (keys %auto)
   {
     print $_ . " : ";
     print $auto{$_} if defined $auto{$_};
     print "\n";
   }

Es empfiehlt sich daher auch immer, den Wert mittels defined zu überprüfen, ob er vorhanden ist. Ob ein Schlüssel überhaupt existiert können Sie mit dem Schlüsselwort exists überprüfen ...

foreach (keys %auto)
   {
     print $_ . " : " if exists $auto{$_} ;
     print $auto{$_}  if defined $auto{$_};
     print "\n";
   }

Mit der Funktion each() können Sie ähnlich wie bei der foreach Schleife alle Schlüssel-Werte-Paare durchlaufen. Sind keine Paare mehr vorhanden, liefert each false als Rückgabewert zurück ...

#!/usr/bin/perl -w

%auto = (Marke => "BMW", Baujahr => 1999, PS => 113, KFZ => "A - BC111");

while( ($schluessel, $wert) = each %auto){
   print $schluessel . " : " . $wert . "\n";
  }

Zum Schluss sollten noch einige Punkte erwähnt werden, wofür sich Hashes besonders gut eignen ...

Als letztes Beispiel werden Sie das Skript zu Feststellung der Wörterhäufigkeit sehen ...

#!/usr/bin/perl -w

#Text anhand Whitespace aufteilen
while(defined($zeile=<>)){
   chomp $zeile;
   my @word = split( m/\s/, $zeile);

  foreach (@word){
     s/\W/ /g; #nichtwortzeichen in $_ entfernen
     next if $_ eq ' ';
     $hash{$_}++; #Wert um eins erhöhen
  }
 }

foreach(sort { $hash{$b} <=> $hash{$a} } keys %hash){
    print "$_ -> $hash{$_}\n";
  }

Entscheidend in diesem Programm ist die Zeile...

$hash{$_}++

Ist der Schlüssel nicht vorhanden wird er neu erstellt und der Wert wird um 1 inkrementiert. Ist der Schlüssel vorhanden wird nur der Wert um 1 inkrementiert. Wir geben die Worthäufigkeit sortiert nach Menge absteigend aus. In dieses Programm können Sie entweder eine Textdatei umleiten, oder selbst etwas eingeben. Bei der Selbsteingabe müssen Sie allerdings die Eingabe mit der Tastenkombination STRG+Z (für End of file) beenden.

9.2. DBM-Dateien           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Um den Inhalt von Hashes zu speichern wird eine spezielle Datei, ein DBM-Datei, verwendet. Man spricht dabei von einer Persitenz von Hashes. Eine typische Anwendung dafür ist zum Beispiel ein Zähler von Webseiten und längere Berechnungen. Sollte aber mehr als ein Zugriff auf diese Datei erfolgen, wie dies bei Statistiken von Webseiten häufig vorkommt, müssen Sie dafür sorgen, dass diese Datei während diese Zugriffs gesperrt wird.

Damit eine Änderung des Hashes in der DBM-Datei auch geschrieben wird, benötigen Sie die Funktion tie() Um diesen Zustand wieder Rückgängig zu machen haben Sie die Funktion untie() Bitte beachten Sie das tie() nicht die DBM-Datei sperrt auf die Zugegriffen wird. Dafür sind Sie verantwortlich. Außerdem benötigen Sie dafür das Modul SDBM_File oder DB_File. Hierzu nun ein kleines Anwendungsbeispiel ...

#!/usr/bin/perl -w

use SDBM_File;
use Fcntl;

tie(%email, 'SDBM_File', './dbmfile', O_RDWR|O_CREAT , 0640)
     or
     die "Fehler bei tie.........!\n";

print "Bitte geben Sie den Namen ein : ";
chomp( $name=<>);
print "Bitte geben Sie die E-Mail-Adresse ein : ";
chomp( $mail=<>);

$email{$name} = $mail;

print "Inhalt der DBM-Datei : \n";
print join ("\n" ,map { "($_ => $email{$_})" } sort keys %email), "\n";

untie(%email);

In Ihrem Verzeichnis finden Sie jetzt eine Datei mit der Endung *.dir (Indexdatei) und *.pag (Datendatei). Beide Dateien werden benötigt, damit die DBM-Library die Datei auslesen bzw. ändern kann. Sollten Sie das Modul DB_Files verwenden ist dies nicht der Fall. Das Modul DB_Files stellt auf alle Fälle die schnellere Version dar.  Hier wurde das Modul SDBM_File verwendet, weil es garantiert auf jedem System vorhanden ist.

Der Vorteil von DBM-Dateien liegt eindeutig an der Performance. Normalerweise müssten diese Dateien zuerst in einem virtuellen Speicher angelegt werden. Und solche Dateien können sehr groß werden. Durch die Funktion tie() wird versichert, dass kein virtueller Speicher verwendet, sondern alles gleich in die DBM-Datei geschrieben wird. Dies wirkt sich besonders gut in der Performance aus, wenn Sie z.B. mehrer Mega Bytes an Textdateien mit einem Wörterbuch, auf die richtige Schreibweise, überprüfen wollen.

9.3. Funktionen sort und grep           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Zwei sehr nützliche und häufig eingesetzte Funktionen sind grep zum Suchen und sort zum Sortieren von Arrays oder Hashes.

Zuerst die Funktion sort zum Sortieren. Der Einsatz dieser Funktion sieht so aus ...

@ARRAY_SORTIERT = sort(@ARRAY_UNSORTIERT);

Die Funktion sort sortiert die Einzelnen Elemente einer Liste lexikographisch, was genauso abläuft wie bei einem Lexikon. Dies ist allerdings beim sortieren von Zahlen ein Nachteil. Warum, sehen Sie an diesem Programm ...

#!/usr/bin/perl -w

@unsortiert = (2, 11, 55, 1234, 3, 6, 9);
@sortiert = sort(@unsortiert);

print "@sortiert" , "\n";

Die Zahlen werden anhand der Ziffernfolge sortiert. Somit wird zuerst nach der Zahl 1 sortiert. In unserem Beispiel kommt damit 11 vor 2, da diese Zahl mit 1 beginnt.

Ein weiteres Beispiel von sort mit Arrays von Zeichenketten ...

#!/usr/bin/perl -w

@unsortiert = ("Zebra", "Gepard", "Baer", "Affe", "Aal");

@sortiert = sort(@unsortiert);

print "@sortiert" , "\n";

Und noch ein Beispiel eines Hash der einmal  nach Schlüssel und einmal nach Werten sortiert wird ...

#!/usr/bin/perl -w

%adressen = ("Name" => "D.J.",
             "Wohnort" => "Palermo",
             "Alter" => 66,
             "Bemerkung" => "A good people");


#Geben sortiert nach Werten aus ...
foreach (sort { $adressen{$a} cmp $adressen{$b} } keys %adressen)
 {
   print $_ . " : " . $adressen{$_} . "\n";
 }

 print "\n";

#Sortiert nach Schlüsseln
foreach (sort keys %adressen)
 {
   print $_ . " : " . $adressen{$_} . "\n";
 }

Was passiert hier ...

foreach (sort { $adressen{$a} cmp $adressen{$b} } keys %adressen)

Sieht kryptischer aus als es ist. Hier werden zwei Werte ausgewertet. Der Perl-Interpreter ließt Ausdrücke von rechts nach links. Also bei dem Hash %adressen wir holen uns den Schlüssel mit keys. Der Wert wird in $adressen{$b} gelegt und ein anderer in $adressen{$a} Diese beiden werden nun miteinander verglichen und mit sort lexikographisch sortiert. Der Sortierte Wert steht jetzt in der Standartvariablen $_ und wird anschließend ausgegeben.

Die Variablen $a und $b sind übrigens weitere Standardvariablen, die für das Sortieren verwendet werden. Folglich können Sie diese beiden Standardvariablen auch mit Zahlen verwenden. Somit werden anders als in dem Beispiel oben, die Zahlen der Größe nach sortiert ...

#!/usr/bin/perl -w

@unsortiert = (2, 11, 55, 1234, 3, 6, 9);
@sortiert = sort{$a <=> $b} @unsortiert ;

print "@sortiert" , "\n";

Sollten Sie die Sortierung in umgekehrter Reihenfolge vornehmen wollen, also der größere Wert zuerst, müssen Sie nur die Standardvariablen $a und $b bei Ihrer Angabe umdrehen ...

#!/usr/bin/perl -w

@unsortiert = (2, 11, 55, 1234, 3, 6, 9);
@sortiert = sort{$b <=> $a} @unsortiert ;

print "@sortiert" , "\n";

... oder ...

#!/usr/bin/perl -w

@unsortiert = qw(America Zebra Aal Vogel Tor Feuer);
@sortiert = sort{$b cmp $a} @unsortiert ;

print "@sortiert" , "\n";

Mit der Funktion grep filtern Sie aus einer Liste alle Elemente heraus für die ein Ausdruck falsch ist. Hier der Syntax zu grep ...

@liste = grep( AUSDRUCK, LISTE);

Mit grep durchschleifen Sie alle Elemente der Liste und Werte, jedes Element mit einem gegebenen Begriff aus. Ist der Ausdruck wahr, fügt grep das Element, der sich daraus ergebenen Liste hinzu. Hier ein Beispiel für grep ...

#!usr/bin/perl -w

@liste1 = (1..10);
@liste2 = qw(a b c d e a b c e f a a);
@temp = (0);

@temp = grep($_ %2, @liste1);     #alle ungeraden Zahlen in @temp
print @temp , "\n";

@temp = grep($_ ne "a", @liste2);  #alle Buchstaben außer 'a' in @temp
print @temp , "\n";

Die Funktion grep setzt  die Variable $_ zu dem aktuellen Listenwert und wertet diesen Ausdruck aus.

grep lässt sich auch im Skalaren Kontex aufrufen ...

#!/usr/bin/perl -w

@files = <*.txt>;

@file = grep { -s > 1*1024} @files;

print join("\n" , @file)

In diesem Beispiel werden alle Textdateien mit der Endung *.txt und größer als 1024 Bytes ausgegeben. Ebenso lässt sich grep hervorragend mit dem Pattern Matching-Operator m// einsetzten ...

#!/usr/bin/perl -w

@files = <>;

(@file) = grep { m/.*?(\/\*.*?\*\/).*$/ } @files;

print join("\n" , @file);

Mit diesem Programm geben Sie alle Zeilen aus, wo ein Text zwischen den Zeichen /* Text */ steht. Also Kommentare eines C Programm oder einer Headerdatei. Zum Beispiel ...

/usr/bin/perl -w list.pl  /usr/include/stdio.h

... gibt alle Kommentare aus, die in der Headerdatei stdio.h stehen.