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 ...
- Häufigkeitsermittlung - Ein Schlüssel bekommt einen String und den passenden Wert der die Häufigkeit des Vorkommens zählt.
- Realisierung von Mengen
- komplexe Sortieroperationen
- benannte Subroutinenparameter
- Multihashes
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.
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.
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.
|