Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Kompendium der Informationstechnik
 von Sascha Kersken
EDV-Grundlagen, Programmierung, Mediengestaltung
Buch: Kompendium der Informationstechnik
gp Kapitel 6 Konzepte der Programmierung
  gp 6.1 Algorithmen und Datenstrukturen
    gp 6.1.1 Ein einfaches Praxisbeispiel
    gp 6.1.2 Sortier-Algorithmen
    gp 6.1.3 Such-Algorithmen
    gp 6.1.4 Ausgewählte Datenstrukturen
  gp 6.2 Reguläre Ausdrücke
    gp 6.2.1 Muster für reguläre Ausdrücke
    gp 6.2.2 Programmierung mit regulären Ausdrücken
  gp 6.3 Systemnahe Programmierung
    gp 6.3.1 Prozesse und Pipes
    gp 6.3.2 Threads
  gp 6.4 GUI- und Grafikprogrammierung
    gp 6.4.1 Zeichnungen und Grafiken erstellen
    gp 6.4.2 Animation
    gp 6.4.3 Programmierung fensterbasierter Anwendungen
    gp 6.4.4 Java-Applets
  gp 6.5 Zusammenfassung

gp

Prüfungsfragen zu diesem Kapitel (extern)


Galileo Computing

6.2 Reguläre Ausdrücke  downtop

Bereits in Kapitel 4, Betriebssysteme, wurden Beispiele für die Mustererkennung in der UNIX-Shell und im Programm grep gezeigt. In der Softwareentwicklung spielt die Arbeit mit Textmustern eine sehr wichtige Rolle. Denken Sie nur einmal an die Verarbeitung zahlreicher Personendaten oder Verwaltungsinformationen bei Behörden oder großen Unternehmen. Es wäre mehr als mühselig, solche Datenbestände mit Hilfe konventioneller Einzeltextvergleiche in den Griff bekommen zu wollen.

Deshalb wurde ein besonderes Verfahren entwickelt: die Arbeit mit regulären Ausdrücken. Der Begriff »regulärer Ausdruck« ist eigentlich ein wenig unglücklich gewählt. Es handelt sich um die Übersetzung des englischen Wortes »regular expression« (abgekürzt RegExp), das man besser als »Ausdruck mit Regeln« wiedergeben würde – »regulärer Ausdruck« klingt ein wenig wie »gebratener Würstchenverkäufer«.

Reguläre Ausdrücke in Perl

Die beste Unterstützung für reguläre Ausdrücke bietet traditionellerweise die Programmiersprache Perl, denn sie wurde vor allem zu diesem Zweck entworfen. Die Syntax für reguläre Ausdrücke, die in Perl eingesetzt wird, geht weit über die Möglichkeiten von grep hinaus. Sie stammen zum großen Teil von den UNIX-Textverarbeitungssprachen sed und awk. Inzwischen werden reguläre Ausdrücke auch von Java und dem Microsoft-.NET-Framework unterstützt. Da sie das Programmieren erheblich erleichtern können, wird ihnen an dieser Stelle ein ganzer Abschnitt gewidmet.

In Perl werden vor allem zwei wichtige Operatoren zusammen mit regulären Ausdrücken verwendet:

gp  m/RegExp/ sucht in einem String nach dem Vorkommen von RegExp. In vielen Fällen kann das m vor dem / einfach weggelassen werden.
gp  s/RegExp/String/ ersetzt ein gefundenes Vorkommen von RegExp durch den angegebenen String.

Es ist nicht so schwierig, das Funktionieren dieser Operatoren als solches zu verstehen; wichtiger ist es, zu lernen und sich zu merken, welche Formulierungen in regulären Ausdrücken zulässig sind.


Galileo Computing

6.2.1 Muster für reguläre Ausdrücke  downtop

Grundsätzlich existieren die folgenden Komponenten für einzelne Zeichen, aus denen Sie in Perl reguläre Ausdrücke zusammensetzen können:

gp  abc steht einfach für die genannten Zeichen selbst.
gp  [abc] steht für ein beliebiges Zeichen aus der Liste. [MH]aus passt also auf »Haus« und »Maus«, aber nicht auf »raus«.
gp  [a-z] steht für die gesamte angegebene Zeichenfolge im Zeichensatz. Beispielsweise bedeutet [a-z], dass alle kleinen Buchstaben gemeint sind, [0-9] steht dagegen für alle Ziffern und so weiter. Natürlich müssen sie nicht immer eine ganze Zeichenkategorie angeben: [A-K] repräsentiert zum Beispiel alle großen Buchstaben von A bis K. Außerdem können Sie innerhalb desselben Paares eckiger Klammern sowohl einen Bereich als auch einzelne Zeichen eingeben. [a-dp] trifft etwa auf alle Zeichen von a bis d sowie auf das p zu. Auch mehrere Bereiche sind möglich: Beispielsweise stellt [0-9a-fA-F] sämtliche Hexadezimalziffern dar.
gp  [^abc] steht für jedes beliebiges Zeichen außer denjenigen in der Liste. So trifft [^M]aus wieder auf »Haus« und »raus«, aber nicht auf »raus« zu. Übrigens passt dieser Ausdruck auch nicht auf »aus«, weil zwar ein bestimmtes Zeichen nicht verwendet werden darf, aber dennoch ein Zeichen benutzt werden muss.
gp  ? bedeutet, dass das links davon stehende Zeichen beziehungsweise die Liste optional ist; sie darf also vorkommen oder auch nicht. Hau?se kann also für »Hase« oder für »Hause« stehen, aber nicht für »Hanse«.
gp  * bedeutet, dass das links davon stehende Zeichen beziehungsweise die Liste beliebig oft vorkommen darf, also gar nicht, einmal oder öfter. 12*3 könnte also 13, 123 oder 12.223 heißen.
gp  + bedeutet, dass das links davon stehende Zeichen beziehungsweise die Liste mindestens einmal vorkommen muss, aber auch öfter vorkommen darf. 0,3+ kann also etwa für 0,3, 0,333 oder 0,333333333 stehen.
gp  {n} bedeutet, dass das links davon stehende Zeichen beziehungsweise die Liste genau n-mal vorkommen muss. Beispielsweise ist [0-9]{5} das Muster für eine deutsche Postleitzahl: fünf Ziffern zwischen 0 und 9.
gp  {m,n} bedeutet, dass das links davon stehende Zeichen beziehungsweise die Liste mindestens m-mal und höchstens n-mal vorkommen darf. Ein sinnvolles Muster für Telefonnummern ohne Vorwahl ist beispielsweise [0-9]{4,8}.

Damit Sie die bisherigen Informationen leichter verarbeiten können, sehen Sie hier zunächst ein kurzes Beispiel. Das folgende Programm überprüft bei einer eingegebenen Zeile, ob es sich dabei um eine Ganzzahl handelt:

#!/usr/bin/perl –w
print "Geben Sie eine ganze Zahl ein: ";
$zahl = <>;
if ($zahl =~ /\-?[0-9]+/) {
   print "Danke für die Ganzzahl!\n";
} else {
   print "Das ist keine Ganzzahl!\n";
}

Sehen Sie sich zunächst einmal genauer das verwendete Muster an:

/\-?[0-9]+/

Sonderzeichen

Der erste Teil des Ausdrucks, \-?, bedeutet, dass zu Beginn der Zahl ein Minus stehen kann oder auch nicht. Das Minuszeichen hat innerhalb regulärer Ausdrücke eine besondere Bedeutung, deshalb müssen Sie einen Backslash davor setzen, wenn Sie das Zeichen als solches meinen. Dies ist in regulären Ausdrücken übrigens bei einer Reihe von Zeichen der Fall: – + ? * ( ) [ ] { } / \ | , . ^ $. Darüber hinaus müssen Sie noch % und @ schützen, weil sie in Perl ein Hash beziehungsweise Array einleiten.

Nach dem optionalen Minuszeichen können beliebig viele Ziffern folgen. Dazu wird zunächst in eckigen Klammern die Folge 0 bis 9 angegeben, gefolgt von einem Pluszeichen für ein oder mehr Vorkommen.

Neben den bisher besprochenen Formen für einzelne Zeichen existieren etliche interessante Kurzfassungen und spezielle Formulierungen, von denen hier nur die wichtigsten wiedergegeben werden:

gp  \s steht für beliebigen Whitespace, das heißt für Leerzeichen, Tabulatoren oder Zeilenumbrüche (falls überhaupt mehr als eine Zeile auf einmal verarbeitet wird; siehe unten).
gp  \w repräsentiert sämtliche Arten von Zeichen, die in Wörtern vorkommen können. Genauer gesagt sind alle Zeichen zulässig, die in Perl-Bezeichnern vorkommen können: Buchstaben, Ziffern und _ (Unterstrich).
gp  \W ist das Gegenteil von \w: Es trifft auf sämtliche anderen Zeichen zu.
gp  \d steht für sämtliche Ziffern, ist also eine Abkürzung für [0-9].
gp  \D stellt dagegen sämtliche Zeichen dar, die keine Ziffern sind; dies ist eine Abkürzung für [^0-9].
gp  \b trifft auf Wortgrenzen zu. Diese Komponente ist also überall dort zu finden, wo ein Wort beginnt oder aufhört. Zum Beispiel findet \ber die Wörter »er« oder »Kölner«, aber nicht »Kölnern«.
gp  \B betrifft dagegen sämtliche Stellen innerhalb von Wörtern; ein Muster, das Sie mit \B kombinieren, wird also nur dann gefunden, wenn es nicht am Anfang oder am Ende eines Wortes steht. \Ber findet also nur »Kölnern«, nicht »Kölner« oder »erklären«.
gp  () dient dazu, einen Teilausdruck zu einer Gruppe zusammenzufassen. Dies hat zum einen den Zweck, ganze Gruppen durch Zähler wie +, ? und * zu versehen oder mittels | Optionen anzugeben; zum anderen merkt sich die Perl-RegExp-Engine geklammerte Ausdrücke in den speziellen Variablen $1 bis $9, um sie beim Ersetzen in den Ersatztext mitaufzunehmen.
gp  ^ außerhalb eckiger Klammern steht für den Zeilenanfang. ^H trifft also beispielsweise auf den Text »Hallo Welt!« zu, aber nicht auf den Satz »Sag’ Hallo zur Welt!«.
gp  $ steht entsprechend für das Zeilenende. Beispielsweise trifft \d+0$ nur auf Zahlen zu, die mit einer 0 enden.

Das Muster im obigen Programmbeispiel mit der Eingabe einer ganzen Zahl müsste präziser eigentlich folgendermaßen formuliert werden:

/^\-?\d+$/

Das alte Muster ließ nämlich jede beliebige Eingabe zu, die an irgendeiner Stelle eine ganze Zahl enthält. Diese neue Formulierung überprüft Zeilenanfang und -ende, lässt also nur Eingaben gelten, die aus nichts weiter als einer ganzen Zahl bestehen. Außerdem wurde in dieser neuen Formulierung das umständliche [0-9] durch die äquivalente Form \d ersetzt.

Mit Hilfe geklammerter Ausdrücke können Sie eine noch größere Flexibilität erreichen. Beispielsweise passt der nächste Ausdruck auf »Hund«, »Katze« oder »Maus«:

/(Hund)|(Katze)|(Maus)/

Ein weiteres interessantes Hilfsmittel bei der Arbeit mit regulären Ausdrücken sind diverse Modifikatoren, die hinter dem schließenden Slash geschrieben werden:

gp  /i verzichtet auf die Unterscheidung zwischen Groß- und Kleinschreibung. /[a-z]+/i findet also beispielsweise beliebige Kombinationen von Buchstaben in beiden Schreibweisen.
gp  /g findet mehr als einen Treffer, kann also etwa in einer Schleife abgearbeitet werden oder bei Ersetzungen dafür sorgen, dass nicht nur das erste Vorkommen ersetzt wird, sondern alle.
gp  /e erlaubt die Verwendung eines auszuwertenden Ausdrucks als Ersatztext. Normalerweise ist nur ein einfacher String gestattet.

Galileo Computing

6.2.2 Programmierung mit regulären Ausdrücken  toptop

Nachdem Sie nun die einzelnen Bestandteile regulärer Ausdrücke kennen gelernt haben, müssen Sie als Nächstes die verschiedenen Perl-Funktionen erlernen, in denen reguläre Ausdrücke verwendet werden können.

Matching

Die einfachste dieser Funktionen ist der Matching-Operator m//, der überprüft, ob ein angegebener regulärer Ausdruck in einem String vorkommt oder nicht. Er wird in der Regel zusammen mit einem der beiden Operatoren =~ oder !~ verwendet. Der Wert des Operators =~ ist wahr, wenn der reguläre Ausdruck auf der rechten Seite zu der Variablen auf der linken Seite passt. Das Gegenteil ist bei !~ der Fall: Dieser Operator liefert den Wert wahr, wenn der reguläre Ausdruck auf der rechten Seite nicht zu der Variablen passt.

Beispielsweise können Sie folgendermaßen überprüfen, ob in der Variablen $test Ziffern vorkommen:

print "Ziffern gefunden!" if $test =~ /\d/;

Mit Hilfe der folgenden Anweisung lässt sich dagegen testen, ob $test keine Buchstaben enthält:

print "Buchstabenfrei!" if $test !~ /[a-z]/i;

Wie bereits in Kapitel 5, Grundlagen der Programmierung, kurz erwähnt, wird das m vor dem Ausdruck in der Regel weggelassen. Es ist nur dann wichtig, wenn Sie statt dem / ein anderes Zeichen verwenden, was besonders nützlich ist, wenn der / selbst im regulären Ausdruck vorkommt. Sie können sämtliche Zeichen benutzen, die auch für Quoting-Operatoren wie q// oder qw// zulässig sind, in einem solchen Fall ist das vorangestellte m aber zwingend erforderlich. Beispielsweise können Sie folgendermaßen überprüfen, ob es sich beim Inhalt der Variablen $pfad um einen absoluten UNIX-Pfad handelt:

print "UNIX-Pfad!" if $pfad =~ m|^(/[^/]+)+|;

In der traditionellen Schreibweise wäre es erheblich komplizierter, diese Anweisung zu schreiben, und obendrein wäre sie viel schwerer lesbar:

print "UNIX-Pfad!" if $pfad =~ /^(\/[^\/]+)+/;

Der Ausdruck bedeutet in beiden Fällen: Zu Beginn des Ausdrucks (^) steht ein Slash (/), gefolgt von einem oder mehreren Zeichen außer dem Slash ([^/]+). Diese Abfolge wiederholt sich bei Bedarf mehrere Male (+ außerhalb der Klammern). Im zweiten Fall wirkt es sehr störend, dass der Slash selbst nicht einfach verwendet werden kann, sondern durch einen vorangestellten Backslash zum normalen Zeichen erklärt werden muss.

Muster
ersetzen

Eine weitere Anwendungsmöglichkeit regulärer Ausdrücke ist das Ersetzen der jeweiligen Fundstellen durch einen Ersatztext. Dies geschieht in Perl mit Hilfe des Operators s///. Beispielsweise ersetzt die folgende Anweisung jedes Vorkommen von »Düsseldorf« oder »Leverkusen« in der Variablen $text durch »Köln«:

$text =~ s/(Düsseldorf)|(Leverkusen)/Köln/g;

Der bereits erwähnte Modifikator g sorgt dafür, dass jedes Vorkommen des gesuchten regulären Ausdrucks ersetzt wird.

Durch die Verwendung von Klammern im regulären Ausdruck können Sie außerdem die Speicherung einzelner Teile des gefundenen Textes veranlassen, diese Teile können Sie innerhalb des Ersatztextes verwenden. Beispielsweise könnten Sie eine Angabe von Postleitzahl und Ort nach dem Schema »53229 Bonn« in die Schreibweise »Postleitzahl: 53229, Ort: Bonn« umwandeln:

$ortsangabe =      s/((\d{5})\s+([a-z]+)/Postleitzahl: $1, Ort: $2/i;

Auch zum Extrahieren bestimmter Informationen aus einem Gesamttext sind die Platzhalter $1 bis $9 hervorragend geeignet. Zum Beispiel können Sie folgendermaßen den Inhalt der Textdatei info.txt nach Angaben durchforsten, bei denen es sich sehr wahrscheinlich um E-Mail-Adressen handelt:

open (FH, "<info.txt") || die "Kann nicht öffnen!\n";
while ($line = <FH>) {
   chomp $line;
   while ($line =~ /([a-z0-9\.\-_]+\@[a-z0-9\.\-_]+)/) {
      push (@mails, $1);
   }
}
close FH;
print "Gefundene Mail-Adressen:\n";
foreach $addr(@mails) {
   print "$addr\n";
}

Der reguläre Ausdruck, der hier für E-Mail-Adressen verwendet wird, basiert auf einer Wahrscheinlichkeitsannahme und ist nicht absolut genau. Gefunden wird eine Sequenz aus Buchstaben, Ziffern, Bindestrichen und Unterstrichen, ein @-Zeichen und eine weitere Sequenz aus diesen Zeichen. Einige Administratoren erlauben in E-Mail-Benutzernamen noch andere Zeichen, solche nicht standardkonformen E-Mail-Adressen blieben unentdeckt. Außerdem würde beispielsweise ein Punkt hinter einer E–Mail-Adresse, die am Ende eines Satzes steht, mit aufgenommen.

Eine ähnliche Funktion wie s/// erledigt übrigens der Operator tr///, der Translations-Operator: Er ersetzt jedes Vorkommen des Strings im ersten Teil durch den String im zweiten Teil. Insofern hat er allerdings nichts mit regulären Ausdrücken zu tun.

Weitere praktische Anwendungsbeispiele für die Verwendung von regulären Ausdrücken finden Sie in Kapitel 18, Serverseitig dynamische Websites, und 19, JavaScript.

  

Einstieg in PHP 5

Einstieg in Java

C von A bis Z

Einstieg in C++

Einstieg in Linux

Einstieg in XML

Apache 2




Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de