Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Konvertieren, int ceil sprintf hex oct floor Runden AbschneidenPerl Kurs Skripting Reguläre Ausdrücke Pattern Matching Operator String Matching Metazeichen Reguläre Ausdrücke Pattern Matching Operator String Matching Metazeichen Perl Kurs Kapitel 14

14.1. Suchen mit regulären Ausrücken           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Jetzt kommt ein Interassantes, wenn nicht das Interessanteste Thema in Perl. Der Suche in Texte nach bestimmten Wörtern oder Textpassagen. Speziell als Webmaster von vielen Webseiten hat man es oft satt, alle Seiten nach einem bestimmten Wort zu durchsuchen. Besser noch wenn Sie vorhaben in CGI eine eigene Suchmaschine für Ihre Webseite zu erstellen. Zugegeben mit anderen Programmiersprachen ist dies auch zu bewerkstelligen, nur nicht so kurz und einfach wie in Perl.

Bevor Sie nun beginnen nach Wörtern zu suchen, benötigen Sie eine Textdatei. Welche Sie dabei benutzen bleibt ihre Sache. 

Jetzt folgt das erste Perl-Skript, womit Sie nach einem bestimmten Wort in einer Datei suchen können ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);

open(FH, "< $file")
         or
         die "Konnte $file nicht öffnen\n";

undef $/;

my $search = <FH>;
 if($search =~ m/C-KURS/) {
         print "C-KURS gefunden\n"
         }

Bis zu undef $/ nichts neues für Sie. Was bedeutet nun das undef $/ ? Wir setzen damit die Standardvariable $/ auf einen undefinierten Wert, womit Sie den "Schlürfmodus" des Eingabeoperators aktivieren. Dies sorgt dafür, dass Sie den gesamten Text in der Variable $search stehen haben. Nun wird mittels ...

$search =~ m/C-KURS/

... nach dem Wort C-KURS gesucht. Dies geschieht mit dem Pattern Matching Operator ...

m//

Das zu suchende Wort steht dabei zwischen den /-Strichen. Sollte das von Ihnen gesuchte Wort, folgende Zeichen enthalten ...

^ . $ | * + / ( ) [ ] { } ?

... müssen Sie diese mit einem vorangestellten Backslash darstellen oder zwischen geschweifte Klammern stellen ...

$search =~ m{$dollar} ;          #Suchbegriff: $dollar

Der Pattern-Matching Operator m// kann auch abgekürzt werden durch // ...

$search =~ m/C-KURS/;

... bezogen ist diese Schreibweise ...

$search =~ /C-KURS/;

... also die selbe.

Außerdem gibt es auch die Möglichkeit die Schrägstriche des m// - Operators durch andere Zeichen zu ersetzten. Dies hat den Vorteil, dass man z.B. auf die Backslashs in folgenden Beispiel ...

$pfad =~ /\/usr\/bin\/perl/;

... verzichtet werden kann. Nehmen Sie als Beispiel folgendes Programm ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);
print "Wonach wollen Sie in $file suchen : ";
chomp(my $suche=<>);

open(FH, "< $file")
         or
         die "Konnte $file nicht öffnen\n";

undef $/;

my $search = <FH>;
 if($search =~ m/$suche/) {
           print "... gefunden!\n";
           }

Jetzt nehmen Sie an, der Benutzer dieses Programms sucht in einer HTML-Datei nach dem Text </FORM>. Sie wissen zwar jetzt, dass Sie für die Suche folgende Eingabe machen müssen ...

<\/FORM>

Aber die meisten Anwender, werden dies nicht wissen. Also ersetzen Sie einfach die beiden Schrägstriche durch ein anderes Zeichen. Anstatt ...

if($search =~ m/$suche/)

... schreiben Sie hier einfach ...

if($search =~ m{$suche})

... oder ...

if($search =~ m,$suche,)

In diesen Fällen ist aber das Führende m notwendig.

Ein kleiner Tipp noch am Rande ...

my $search = <FH>;
 if($search =~ m{$suche}) {
           print "...... gefunden!\n";
           }

... lässt sich auch Perltypisch verkürzt schreiben ...

print "... gefunden\n" if <FH>=~ m{$suche};

Soll die Suche negiert werden, müssen Sie anstatt =~ einfach die Schreibweise !~ verwenden. Für unser Beispiel hieße dies dann ...

print "... $suche nicht enthalten\n" if <FH> !~ m{$suche};

Hier wird also die print-Anweisung nur Ausgeführt, wenn das Gesuchte Wort $suche nicht im Text enthalten ist.

Noch etwas, was Sie zu Pattern-Matching wissen sollten. Wenn Sie das Pattern leer lassen, wird das Muster verwendet, dass Sie bei der letzten Suche erfolgreich verwendet haben. Hier das Programmbeispiel ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);
print "Wonach wollen Sie in $file suchen : ";
chomp(my $suche=<>);

open(FH, "< $file")
         or
         die "Konnte $file nicht öffnen\n";

undef $/;

$_=<FH>;
print "\"$suche\"... gefunden\n" if $_=~ m/$suche/;
print "Viele Zeilen später ...\n";
print "\"$suche\"... erneut gefunden\n" if $_=~m//;

Und wenn wir schon die Standardvariable $_ verwenden, können wir auch auf =~ verzichten. Also ...

$_=<FH>;
print "\"$suche\"... gefunden\n" if /$suche/;
print "Viele Zeilen später ...\n";
print "\"$suche\"... erneut gefunden\n" if //;

Der Nachteil an dem Beispiel zuvor ist aber der, dass sobald Sie das entsprechende Wort gefunden haben, die Suche eingestellt wird. Dies ist die Voreinstellung von Pattern Matching. Um die Suche fortzusetzen, ob mehrere des gesuchten Suchwortes vorhanden sind, müssen Sie nur die Option g am Ende des Pattern Matching Operator hinzufügen ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);

open(FH, "< $file")
 or
 die "Konnte $file nicht öffnen\n";


while(my $search = <FH>){        #Zeilenweise einlesen
            if($search =~ m/und/g) {   #Suchen nach dem Wort: und
                     print $. , ".Zeile: \"und\" gefunden\n"
                     }
            }

Mit der Option g am Ende von ...

$search =~ m/und/g

... weisen Sie die Suchoption zur globalen Suche an, die nacheinander alle Vorkommen findet. Wenn Sie jetzt auch noch wollen, dass keine Unterscheidung zwischen Groß- und Kleinschreibung statt findet, müssen Sie  nur noch zusätzlich die Option i hinten anhängen ...

#Suchen nach dem Wort: und | unabhängig von Groß/Kleinschreibung
$search =~ m/und/gi;

Mit folgenden Optionen können Sie den Pattern Matching Operator  ausstatten ...

Nun besteht die Möglichkeit, einen Text nach einem bestimmten Muster abzusuchen mit Hilfe von speziellen Zeichen die mit Regulären Ausdrücken verwenden können. Aus einer Datei soll nun nach den Worten Systemprogrammierung und Hardwareprogrammierung gesucht und entsprechende Zeilen ausgeben werden ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);

open(FH, "< $file")
         or
         die "Konnte $file nicht öffnen\n";


while(my $search = <FH>){
           if($search =~ m/(Hardware|System)programmierung/g) {
                   print $. , ".Zeile: $search gefunden\n"
                  }
           }

Was wurde den hiermit gemacht ...

if($search =~ m/(Hardware|System)programmierung/g)

Dies stellt eine verkürzte Schreibweise von ...

if( ($search =~ m/Systemprogrammierung/g) ||
    ($search =~ m/Hardwareprogrammierung/g))

... da. Beispielsweise mit ...

/(b|d|g|m)ig/

... suchen Sie hier nach den Wörtern big, dig, gig, mig. Was ist aber, wenn Sie alle Wörter ausgeben wollen in denen die Textfolge "programm" vorkommt? Hier das Skript  ...

#!/usr/bin/perl -w

use strict;

print "Welche Datei wollen Sie durchsuchen : ";
chomp(my $file=<>);

open(FH, "< $file")
         or
         die "Konnte $file nicht öffnen\n";


while(my $search = <FH>){
            if($search =~ m/.programm./gi) {
                   print $. , ".Zeile: $search gefunden\n"
                  }
      }

Somit werden alle Zeichenfolgen ausgegeben, in denen sich die Zeichenfolge "programm" befindet. Und dies bedeutet schon um einiges mehr gefundener Wörter in der Textdatei. Dieses wurde die Zeichengruppe '.' verwendet, dass ein beliebiges Zeichen sein kann außer \n. Wenn Sie die Option s Mitanschließen wird auch dieses Zeichen miteingelesen.

Bevor Sie mit dem Kapitel weitermachen, folgt ein Überblick, mit welchen Zeichen man welche Regulären Ausdrücke darstellen kann ...

Jetzt folgen ein paar Beispiele zum besseren Verständnis wie man dies mit Regulären Ausdrücke anwendet. Beispiel ...

if($variable =~ /^ba(na){2, 4} $/)

Ich interpretiere :

Das bedeutet der Ausdruck in der if-Anweisung ist wahr wenn das Wort 'banana' (2x) 'bananana' (3x) oder 'banananana' (4x) lautet. Der Ausdruck könnte also genauso lauten ...

if($variable =~ /^ba(nana|nanana|nananana) $/)

Beide sind gleichwertig. Nächstes Beispiel ...

if($variable =~ /\bProgramm\b/)

Damit suchen Sie exakt nach dem Wort Programm und nicht Programme oder Programmierer. Dies funktioniert daher, weil hier mit \b ein Wortbegrenzer eingesetzt wurde.

Weiteres Beispiel ...

$variable = " Juli 15, 2001, "
($monat, $tag, $jahr) = $variable =~ /\s*(\S*)\s+(\d+)\D+(\d{4})/;

Wir interpretieren wieder ...

Weiteres Beispiel ...

#!/usr/bin/perl -w

use strict;

print "Bitte geben Sie eine mindestens 4 Stellige Zahl ein : ";
chomp(my $zahl=<>);

if($zahl =~ m/\d{4}?/) {
      print "Zahl OK\n";
     }
else{
      print "Der Zahlbereich wahr kleiner wie 4 Stellen\n";
     }

Das Zeichen ^ steht für den Anfang der Zeile und $ für das Ende der Zeile ...

#!/usr/bin/perl -w

use strict;

my $text = "Hallo Welt ich lerne Regulaere Ausdruecke kennen";

$_=$text;
if (/^Hallo/)  {print "Hallo am Anfang des Textes gefunden\n";}
if(/Hallo/)    {print "Hallo gefunden\n";}

#wird nicht ausgegeben
if(/^Welt/)    {print "Welt am Anfang gefunden\n"}
if(/Welt/)     {print "Welt gefunden\n";}

if (/kennen$/) {print "\"kennen\" steht am Ende\n";}
if (/kennen/)  {print "\"kennen\" gefunden\n";}
#wird nicht ausgegeben
if (/^kennen/) {print "\"kennen\" steht am Anfang\n";}

print "Der 1. Buchstabe des Strings lautet " ,  /^(.)/ , "\n";
print "Der letzte Buchstabe ist ein ",  /(.)$/  , "\n";

#Gibt kompletten String aus von Anfang ^ bis Ende $
print  /^(.*)$/ , "\n";

Der Reguläre Ausdruck ...

print /^(.*)$/ , "\n";

... sieht schlimmer aus als er ist :

Was ist aber, wenn der Text so aussieht ...

my $text = "Hallo Welt ich lerne\n Regulaere Ausdruecke kennen";

Wie gibt man da den kompletten Text aus, da ja ein Newline-Zeichen dazwischen ist?

print /^(.*)$/mg , "\n";

Sie verwenden einfach das Flag m , was ja bedeutet das Strings aus mehreren Zeilen bestehen können. Und des weiteren verwenden Sie das Flag g damit die Suche weiter nach vorne auf den Anfang der nächsten Zeile verschoben wird.

14.2. Suchen und Ersetzen mit regulären Ausdrücken           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Sie sehen schon, es gibt unzählig viele flexible Möglichkeiten mit den Regulären Ausdrücken. Sie werden noch viele Beispiele dazu sehen und kennen lernen. Doch jetzt zu einer weiteren Möglichkeit von Regulären Ausdrücken. Dem Suchen und Ersetzen. Der Syntax, des Pattern Matching Ausdruck, vom Suchen und Ersetzen sieht folgendermaßen aus ...

$string =~ s/Suchemuster/Ersetzendurch/;

Im Gegensatz zum Suchen alleine ändert sich im Syntax nur das voranstellen von s und es wird direkt im Anschluss des Suchstrings der Ersetzungsstring hinzugefügt.

Beim Suchen und Ersetzten können Sie die selben Option setzen wie schon beim Suchen im Kapitel zuvor. Und ebenso gelten die selben Regulären Ausdrücke wie wir Sie schon im Kapitel zuvor gesehen haben.

Zuerst wieder die einfachsten Möglichkeit des Suchen und Ersetzen ...

#!/usr/bin/perl -w

use strict;

while(<>){
     if($_ =~ s/pronix/PRONIX/g) {
              print "Ersetzt in Zeile " . $. , "\n";
              print $_;
             }
        }

Damit ersetzten Sie alle in einem Text vorkommenden Zeichenketten 'pronix' durch die Großschreibung 'PRONIX'. Als Webmaster hat man recht häufig das Problem, einige Tags, die mehrfach im Text vorkommen, zu Verändern. Dabei kann folgendermaßen vorgegangen werden ...

#!/usr/bin/perl -w

use strict;

my $suche = "background-color:#E0E0E0";
my $ersetze = "background-color:#AEEEEE";

open (FH, "< $file")
          or
          die "Konnte $file nicht öffnen\n";


undef $/;
my $text = <>;
if($text =~ s/$suche/$ersetze/g){
           print FH $text;
           }

Mit dem Aufruf des Skripts von ...

Programmname ./pronix/*.*

... wird bei allen sich im pronix-Verzeichnis befindlichen Dateien, die Farbangabe nach Wunsch geändert.

Neu zu dem Kapitel zuvor kommt das Flag /e hinzu. Mit diesem Flag wird der Ersetzungsstring durch eval() als Perlcode evaluiert. Hierzu ein einfaches Beispiel  ...

#!/usr/bin/perl -w

use strict;

my $text = "Die Summe beträgt zahl";
print "Zahl 1 : ";
chomp (my $zahl1=<>);
print "Zahl 2 : ";
chomp (my $zahl2=<>);
my $summe=$zahl1+$zahl2;

$text =~ s/zahl/$summe/g;

print $text , "\n";

Bei diesem Programm, wird der String $zahl durch den Wert der Variable $summe ersetzt. Soweit alles ok. Was ist aber, wenn Sie den String durch folgende Schreibweise ersetzten wollen ...

$text =~ s/zahl/$zahl1+$zahl2/g;

In diesem Fall wird die Rechnung der beiden Zahlen ersetzt, aber nicht wie hier gewollt, das Ergebnis. Wollen Sie das Ergebnis ausgeben lassen, brauchen Sie nur das Flag /e am Ende angeben. Schon wird der Ersetzungsstring durch die Funktion eval() als Perl-Code interpretiert ...

#!/usr/bin/perl -w

use strict;

my $text = "Die Summe beträgt zahl";
print "Zahl 1 : ";
chomp (my $zahl1=<>);
print "Zahl 2 : ";
chomp (my $zahl2=<>);

$text =~ s/zahl/$zahl1+$zahl2/e;

print $text , "\n";

Dieses Flag wir hauptsächlich dazu verwendet, um die URL's wie z.B. ...

%28webmaster%40pronix.de%29

... durch folgenden Code zu encodiert ...

s/%([0-9A-Fa-f]2) / pack("c", hex($1))/eg;

Somit sieht die Ausgabe nach Ersetzung folgendermaßen aus ...

(webmaster@pronix.de)

Mehr zur Dekodierung und Eincodierung gibt es im Kapitel der CGI-Programmierung.