regex0a0.png

Edit v4.000 from 2007-10-10 to 2019-11-25 by SBa+HSc+TSc

Reguläre Ausdrücke

Mit regulären Ausdrücken (auch RegEx oder RegExp genannt) ist es möglich, in beliebigen Texten mit sogenannten Patterns (= Mustern) zu suchen und optional diese durch ein weiteres Muster zu ersetzen. Da es sehr viele, verschiedene Implementationen von regulären Ausdrücken gibt, werden hier nur die Bestandteile beschrieben, die in allen Implementationen funktionieren. Dadurch bekommt man einen guten Einstieg in die Benutzung dieses sehr mächtigen Werkzeugs. Weiterhin gibt es eine Auflistung, der von uns benutzten regulären Ausdrücke und ein Praxisbeispiel, wie man damit auf der Linux-Konsole arbeitet.

Die Reguläre Ausdrücke auf der

Edit v4.000 from 2019-11-20 to 2019-11-25 by TSc

Grundlagen (der kleinste, gemeinsame Nenner)

In die Welt der regulären Ausdrücke hereinzukommen, ist alles andere als leicht. Es gibt zwar sehr viele Anleitungen im Internet dafür, aber meistens sind diese sehr dürftig umgesetzt, oder zu komplex beschrieben. Dann kommt noch dazu, dass es sehr viele verschiedene Arten von Implementationen gibt. Das bedeutet man kann das neu gelernte nicht wirklich anwenden, weil die Implementation, wo man das gelernte einsetzen will, das gelernte nicht voll unterstützt oder etwas ganz anderes dabei herauskommt, als man erwartet. Auf dieser Seite versuchen wir, ihnen zu erklären was reguläre Ausdrücke sind, wie diese kryptischen Zeichenfolgen zu interpretieren sind und eine Beschreibung aller Steuerzeichen, um damit von Grund auf einen eigenen regulären Ausdruck bilden zu können. Wir beschränken uns dabei auch nur auf die Möglichkeiten, die in so gut wie allen Implementationen funktionieren. Anmerkung: Aktuell gibt rund 20 verschiedene Implementationen, wovon einige sogar so exotisch sind, das dass hier gezeigte nicht ohne weiteres funktioniert.

Die Zeichenfolgen der regulären Ausdrücke spalten sich prinzipiell in 2 Arten auf:

  1. Klartext und
  2. Steuerzeichen.

Die Steuerzeichen selbst werden mit Symbolen wie Klammern und mathematischen Zeichen beschrieben. Diese dienen zur Beschreibung eines Suchmuster. Das bedeutet somit auch, wenn man als Suchmuster nur Klartext angibt, dann funktioniert das Suchmuster auch genauso wie eine Klartextsuche. Hat man allerdings im Text auch Symbole dabei, die als Steuerzeichen für die regulären Ausdrücke verwendet werden, dann muss man diese Escapen, d.h. man muss dem jeweiligen Symbol meistens dann ein \-Symbol (Backslash) voranstellen.

Gehen wir nun mal auf die Steuerzeichen ein, die sind das was die regulären Ausdrücke so mächtig machen, bzw. auch so kryptisch. Davon gibt es nicht mal so viele. Zu wissen wofür sie da sind ist aber fundamental für das Verständnis. Zuerst Liste ich diese erst einmal auf, mit einer Beschreibung was sie tun und einem kurzen abstrakten Beispiel dazu. Das, was es so komplex macht sind auch nicht die einzelnen Bedeutungen der Steuersymbole an sich, sondern die Kombination derer.

^
Mit dem Zirkumflex fixiert man den Anfang einer Zeichenkette, bzw. den Anfang einer Zeile. So bedeutet z.B. ^abc das nur Muster erkannt werden, die in einer Zeile/Zeichenkette mit abc beginnen.
$
Mit dem Dollar-Zeichen fixiert man das Ende einer Zeichenkette, bzw. das Ende einer Zeile. So bedeutet z.B. abc$ das nur Muster erkannt werden, die in einer Zeile/Zeichenkette mit abc enden.
.
Der Punkt ist eine sogenannte Zeichenauswahl. Das bedeutet, der Punkt steht für alle druckbaren Zeichen. Das Zeichen kann man auch wie in einem Kartenspiel als Joker sehen und wird deshalb auch oft Wildcard genannt. Welche Zeichen genau dem Punkt zugeordnet sind, kann sich je nach Implementation unterscheiden. Wenn man z.B. in einem Text nach . sucht, findet es quasi jedes druckbare Zeichen und zwar immer eins davon. Wenn man nach einem Punkt selbst sucht, muss dieser logischerweise Escaped werden, also \..
+
Das Plus-Zeichen ist eine Längenbeschreibung, die man z.B. dem Punkt anhängen kann und bedeutet: Finde mindestens 1 oder mehr Zeichen. Das heißt z.B. wenn man nach .+ sucht, dann findet man alles was mindestens ein Zeichen lang ist. Das Plus-Zeichen bezieht sich stets auf den voran gegangenen Ausdruck. Zur Verständlichkeit ein weiteres Beispiel. Wenn man nach a+ sucht, dann findet man alles was alles was mindestens ein a ist und somit auch aa, aaa, usw.
*
Das Stern-Zeichen ist dem Plus-Zeichen sehr ähnlich und bedeutet: Finde mindestens 0 oder mehr Zeichen. Das Stern-Zeichen bezieht sich stets auf den voran gegangenen Ausdruck. Wenn man das zweite Beispiel vom Plus-Zeichen wieder nimmt, aber stattdessen nach a* ist das Ergebnis das selbe, allerdings zählt auch nichts als Suchtreffer.
?
Mit dem Fragezeichen kann man bestimmen, ob der vorangegangene Ausdruck optional ist. Zum Beispiel könnte man damit testen, ob ein URI-Schema http und/oder https ist. Das heißt, wenn man nach https?:// sucht, dann findet man sowohl http://, als auch https://. Das funktioniert deshalb, weil man angegeben hat, das der Buchstabe s optional ist.
|
Der senkrechte Strich ist eine Oder-Anweisung. Wenn man z.B. nach a|b sucht, dann findet man alles, was entweder ein a oder ein b ist. Besonders mit den runden Klammern ist dieses Zeichen sehr nützlich, weil man damit z.B. eine Liste gültiger Wörter beschreiben kann.
()
Mit den runden Klammern kann man Gruppen beschreiben, welche dann z.B. im Ersetzungsmuster platziert werden können, oder auch um eine längere Zeichenkette als optional zu beschreiben. Bei den runden Klammern fängt es auch an richtig komplex zu werden. Als Beispiel nehmen wir mal das, was wir schon vom senkrechten Strich wissen. Wir suchen nach (der|die|das) und finden alle Wörter, die entweder der, die, oder das enthalten. Zusätzlich, wenn man ein Ersetzungsmuster benutzt, wird der erkannte Inhalt in der Klammer zu dem Platzhalter $1 bzw. \1 gepackt. Das heißt, wenn man z.B. die gefunden hat, dann enthält auch $1 bzw. \1 im Ersetzungsmuster das Wort die.
[]
Die eckigen Klammern sind die Zeichenauswahl, das bedeutet, alles was man in die eckigen Klammern schreibt, kann an dieser Zeichenposition gefunden werden. So bedeutet z.B. [0123456789] finde eine Zahl zwischen 0 und 9. Da dies vor allem beim Alphabet dann richtig lang wird, kann man auch einen Bereich mittels dem --Zeichen definieren. So bekommt man genau den selben Ausdruck viel kürzer hin: [0-9]. Die Bereiche orientieren sich nach der Ordnung in der ASCII-Tabelle und ist optimal für Zahlen, Großbuchstaben und Kleinbuchstaben, jedoch nicht auf diese beschränkt. Wichtig bei dieser Zeichenauswahl ist auch, das diese sich immer auf eine Zeichenposition bezieht, das bedeutet, sucht man nach beliebig großen Zahlen, könnte man es mit einer Längenbeschreibung erweitern. Wenn wir also nach [0-9]+ suchen, dann finden wir beliebig große Ganzzahlen, wo aber auch Nullen vorangestellt sein könnten.
{}
Mit den geschweiften Klammern kann man eine Längenbeschreibung exakt definieren, was sehr hilfreich ist, wenn das Plus-Zeichen, oder das Stern-Zeichen nicht ausreicht. Auch diese Längenbeschreibung bezieht sich stets auf den voran gegangenen Ausdruck. Im Prinzip gibt 2Ausdrucksweisen, wie man eine Längenbeschreibung definieren kann. Dabei kommt es drauf an welche Regeln gelten sollen. Hier eine kurze Auflistung, wie man mit der Längenbeschreibung arbeitet:
  • Exakte Länge, z.B. {2} = genau 2 lang.
  • Mindestlänge, z.B. {1,} = mindestens 1 lang, maximal beliebig.
  • Maximallänge, z.B. {0,3} = mindestens 0 lang, maximal 3 lang.
Wie man sehen kann, ist der Maximalwert bei der Mindestlänge nicht beschrieben, das Komma aber gesetzt. Und bei der Maximallänge ist als mindestens eine 0 beschrieben, da der Ausdruck {,3} ungültig wäre. Wenn man darüber nachdenkt, merkt man auch schnell, wie das Plus-Zeichen und das Stern-Zeichen in dieser Längenbeschreibung zu sehen sind:
  • + = {1,} und
  • * = {0,}.
Wenn wir also jetzt wieder unser Beispiel mit den Ganzzahlen nehmen, können wir nun genau bestimmen wie lang diese sein sollen. Als Beispiel, nehmen wir eine beliebige Zahl zwischen 0-9999, also suchen wir nach [0-9]{1,4}. Wichtig dabei ist, auch wenn wir damit Zahlen finden wollen, ist dies im Kontext einer Zeichenkette, d.h. dieser Ausdruck findet auch Zahlen wie 0099. Wenn man sehr explizit einen Zahlenraum beschreiben möchte, ist dies ein durchaus komplexer Ausdruck. Nehmen wir mal als Beispiel den Zahlenraum 1-10, da würde der Ausdruck in etwa so aussehen: ([1-9]|10). Für solche Fälle gibt es aber auch Werkzeuge im Internet, die einem reguläre Ausdrücke für Zahlenbereiche automatisch erzeugen können.
\
Der Backslash wird als sogenanntes Escape-Symbol verwendet. Das bedeutet im Grunde genommen, das man z.B. nach dem Zeichen nach dem Backslash selbst sucht und dieser nicht als Steuersymbol ausgewertet werden soll. Wenn man also nach einem Plus-Zeichen im Text sucht, schreibe man \+. Eine weitere Funktion für das Backslash ist die Beschreibung von Sonderzeichen (auch Metacharakter genannt), also
  • \\ = Ein Backslash selbst (\)
  • \b = Wortbündel (spezifiziert ganzes Wort, z.B. \bKatze)
  • \B = Die Umkehrung vom ganzen Wort, also nicht Anfang oder Ende vom Wort.
  • \d = Alle Zahlen
    \D = Alles außer Zahlen
  • \f = Form feed
  • \n = Line Feed
  • \s = Whitespaces, also mindestens Leerzeichen und Tabulator. Bei vielen Implementationen auch \f, \n, \r und \t.
  • \S = Alles außer \s
  • \r = Carriage Return
  • \t = nur Tabulatoren
  • \w = Alle Buchstaben, Zahlen und _
  • \W = Alles außer \w selbst.
Dies sind normale Sonderzeichzen aus der ASCII-Tabelle, jedoch gibt es noch weitere, welche mit Buchstaben definiert werden. In der Regel sind dies dann weitere Gruppierungen von bestimmen Zeichengruppen. So ist wie in der Liste das \s ein Gruppensymbol für nicht sichtbare, aber druckbare Zeichen, also Leerzeichen und Tabulator. Und das war noch nicht mal alles, es gibt auch eine nummerische Schreibweise, um beliebige Zeichen in der jeweiligen Zeichenkodierung ansprechen zu können. Dies ist vor allem bei UTF-8 und Bytefolgen sehr hilfreich. Folgende Schreibweisen sind möglich:
  • \nnn = Oktale Schreibweise (n = Zahl zwischen 0 und 8; Maximal 3 Stellen lang)
  • \xhh = Hexadezimale Schreibweise (h = Zahl zwischen 0 und F; Nur zweistellig)
  • \uhhhhhhhh = Unicode-Schreibweise (h = Zahl zwischen 0 und F; zweistellig, vierstellig und achtstellig möglich)

Mit dem hier erlangten Wissen sollte man nun in der Lage sein, selbst reguläre Ausdrücke erstellen zu können. Nun folgt noch ein Beispiel, was viele der Symbole kombiniert verwendet.

Aufgabe

Nehmen wir mal an wir haben immer einen Satz der so aufgebaut ist: Ich habe gestern 50,00 Euro bezahlt.. Nun wollen wir aber auch ähnliche Sätze mit einem einzigen Suchmuster erkennen können, also die Währung sollte auch Dollar können, der Satz muss in einer Zeile/Zeichenkette beginnen und enden, der Betrag sollte beliebig groß sein können, der Ausdruck gestern sollte auch stattdessen heute sein können und bezahlt kann stattdessen auch geliehen sein. Der regulären Ausdruck selbst wird in einer Akkordeon-Box versteckt, als mögliche Lösung für diese Aufgabe. Nun noch eine Liste von Beispielsätzen, wo dieser reguläre Ausdruck funktionieren sollte:

Ich habe gestern 50,00 Euro bezahlt.
Ich habe gestern 53,67 Euro geliehen.
Ich habe heute 2 Dollar geliehen.
Ich habe heute 413,25 Dollar bezahlt.
    
Lösung
^Ich habe (gestern|heute) ([0-9]+(,[0-9]{2})?) (Euro|Dollar) (bezahlt|geliehen)\.$

Edit v4.000 from 2019-11-20 to 2019-11-20 by TSc

Muster, die wir benutzen

Hier folgt nun eine Auflistung der Suchmuster und eventuell auch Ersetzungsmuster, die wir schon mehrmals benutzt haben. Möglicherweise sind diese auch für Sie hilfreich.

HTML: Suche nach <div>-Elementen, mit der Klasse BmUla bzw. BmUra und ersetze diese mit <figure>-Elementen in VisualStudio Code.
Ist-Zustand: Soll-Zustand: Suchmuster: Ersetzungsmuster:

Edit v4.000 from 2007-10-10 to 2019-11-20 by SBa+HSc+TSc

Linux-Konsole

Hier gehen wir anhand von Beispielen in das Suchen mit regulären Ausdrücken auf der Linux-Konsole ein. Dazu werden grundlegende Suchmuster in Verbindung mit dem grep-Befehl verwendet. Der grep-Befehl benutzt die Implementation namens POSIX BRE / ERE, das bedeutet, hier werden Musterschnipsel verwendet, die nicht in allen Implementationen funktionieren. Weiterhin wird erläutert, wie man den Suchmustern Daten zuführen kann.

Suche nach 2 Texten

Hier ist ein Beispiel, wie das Muster zur Suche nach 2 Texten, wie sie in einer eMail-Adresse vorhanden sind, erstellt wird:

Die Zeichenkette fängt mit einem oder mehreren alphanumerischen Zeichen, also Buchstaben und Zahlen an:

[[:alnum:]]+

Gefolgt von einem @-Zeichen:

@

Danach kommt eine weitere Zeichenkette:

[[:alnum:]]+

Ein Punkt folgt. Da dieser eine Musterbedeutung hat, wird es mit dem \-Zeichen escapet:

\.

Als letztes kommt eine weitere alphanumerische Zeichenkette, die das Länderkürzel beinhaltet:

[[:alnum:]]+

Zusammen ergibt das mit dem Suchbefehl:

grep -E -o [[:alnum:]]+@[[:alnum:]]+\.[[:alnum:]]+
    

Erläuterungen

Das Beispiel verwendet den grep-Befehl, welcher Ausdrücke in Texten sucht. Der Parameter -E gibt an, das es ein regulärer Ausdruck ist. Der Parameter -o gibt an, das nur der Treffer gezeigt wird, nicht die ganze Zeile.

Hinweis: Eine genaue Beschreibung der Parameter für grep erlangt man mit dem Befehl man grep in der Linux-Konsole.

Der 1. Ausdruck des Suchmusters [[:alnum:]] sucht nach alphanumerischen Zeichen, also a-z, A-Z und 0-9. Das ist ein POSIX-Klammerausdruck. Das folgende +-Zeichen bedeutet, dass wir mindestens eins von den vorhergehenden Zeichen suchen. Das @-Zeichen im Suchmuster sucht nach genau einem @-Zeichen. Danach kommt wieder eine alphanumerische Zeichenfolge [[:alnum:]]. Das ist der Domainname. Der darauffolgende Ausdruck \. sucht nach einem Punkt. Nach genau einem Punkt. Danach kommt eine weitere Zeichenkette, das Länderkürzel.

Wenn also alle diese Teile nacheinander vorkommen, wird der Treffer ausgegeben.


Daten zuführen

...per echo

Das Suchmuster kann auf beliebige Daten angewendet werden, z.B. Webseiten, eMails, Textdokumente, usw.

Hier ist ein Beispiel, wie man eine Zeichenkette in der Kommandozeile bash zur Übergabe definiert und mit dem pipe operator | dem Suchmuster übergibt. Die \-Zeichen am Ende der Zeilen werden nur für die Zeilenumbrüche benutzt. Man kann sie auch weglassen, wenn man alles in eine Zeile schreibt.

echo "Dieser Satz beinhaltet eine eMail Adresse: \
name@domain.de heisst sie." | \
grep -E -o [[:alnum:]]+@[[:alnum:]]+\.[[:alnum:]]+
    

Der 1. Teil echo "..." | sendet einfach die Zeichenkette an den nachfolgenden Teil.

Der Befehl grep sucht nach Mustern und Text in seiner Eingabe. Die angegebenen Parameter sagen grep,

  • dass wir eine erweiterten regulären Ausdruck suchen,
  • dass es die Ergebnisse farblich hervorheben soll und
  • dass nur die genauen Treffer ausgegeben werden sollen.

...per Datei

Um die Suche auf Dateien anzuwenden, kann man folgende Befehle anwenden:

cat DATEI.TXT | grep -E -o [[:alnum:]]+@[[:alnum:]]+\.[[:alnum:]]+
    

oder

grep -E -o [[:alnum:]]+@[[:alnum:]]+\.[[:alnum:]]+ < DATEI.TXT
Beispiel Datum

Hier ein weiteres Beispiel, welches alle Datumseinträge aus einer Datei heraussucht.

Das Datumsformat, das gesucht wird ist: YYYY-MM-TT

Der Name der Datei ist: Bericht.txt

Das Suchmuster stellt zwei neue Konzepte vor:

  1. Anzahl der Vorkommnisse einer Suchmustereinheit:

    {n}

    Die Anzahl der vorherigen Suchmustereinheit, z.B.:

    [[:digit:]]{4}

    bedeutet, dass wir 4 nacheinander folgende Zahlen suchen.

  2. Die Daten werden aus einer Datei ausgelesen:
    Suchbefehl < Datei

Der vollständige Befehl mit Suchmuster lautet somit wie folgt:

grep -E -o [[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2} < Bericht.txt