Mustererkennung mit Regular Expressions (RegEx)

https://oer-informatik.de/regex

https://bildung.social/@oerinformatik/110397061085890869

tl/dr; (ca. 20 min Lesezeit): Die Mustererkennung Regulärer Ausdrücke (RegEx) ermöglicht Automatisierungen an Stellen, wo Handarbeit mühsam und langwierig ist. Telefonnummern in unterschiedlichen Formaten gespeichert? In einer CSV-Datei alle Spalten in verkehrter Reihenfolge? Wo stecken im Fließtext Geldbeträge mit Nachkommastellen? Derlei Aufgaben lassen sich mit RegEx lösen - besser man kennt sie! (Zuletzt geändert am 13.12.2024)

Was sind Reguläre Ausdrücke?

Ein Regulärer Ausdruck (RegEx) ist eine Zeichenkette, die Muster in Zeichenketten mithilfe von syntaktischen Regeln beschreibt.

Reguläre Ausdrücke helfen vor allem dabei, Teilstrings zu finden oder zu ersetzen, die einem einheitlichen Muster entsprechen, sich aber nicht durch einfache Wildcards (wie z.B. ? oder *) ausdrücken lassen.

So lassen sich beispielsweise relativ einfach Muster bilden, die in einem vorhandenen langen Text alle Geldbeträge, Telefonnummern, Internetlinks, EAN-Nummern oder E-Mail-Adressen finden.

Mit RegEx wird man zum Superhelden! (von xkcd, CC BY-NC 2.5 https://xkcd.com/208/)
Mit RegEx wird man zum Superhelden! (von xkcd, CC BY-NC 2.5 https://xkcd.com/208/)

Ein Einstiegs-Beispiel für eine RegEx: die Klassenbezeichnungen an unserer Schule folgen der Logik:

1. Großbuchstabe: Abteilung
2.+3. Großbuchstabe: Ausbildungsberuf
4. Zahl: Einschulungsjahr (letzte Stelle)
5. Großbuchstaben für Parallelklassen
==> 3 Großbuchstaben, eine Zahl, ein Großbuchstabe

Ein Regulärer Ausdruck, der das beschreiben würde, wäre beispielsweise:

[A-Z][A-Z][A-Z][0-9][A-Z]

Es wurden einfach fünf Zeichenklassen hintereinander geschrieben. Das geht noch einfacher:

[A-Z]{3}[0-9][A-Z]
  • [A-Z] definiert hier die Zeichenklasse der Großbuchstaben (alle Zeichen, die in der ASCII/Unicode-Tabelle zwischen A und Z stehen)

  • [0-9] die Zeichenklasse der Zahlen (ASCII zwischen 0 und 9)

  • {3} definiert das mehrfache Vorkommen der Zeichenklasse (ein Quantifizierer).

Suchmuster bestehen also in erster Linie aus Aneinanderreihungen von Zeichenklassen und Quantifizierern.

Grundlagen: Einfache RegEx

Die beiden Grundlagen habe wir ja oben bereits kennengelernt: Zeichenklassen und Quantifizierer. Schauen wir uns diese Elemente mal etwas genauer an:

Definition von Zeichenklassen

Die einfachste Art, Zeichenklassen zu definieren, ist es, alle Zeichen, die man finden will, in eckige Klammern zu schreiben. Die Inhalte in der eckigen Klammer sind ODER-verknüpft.

RegEx Erläuterung
[aeiou],
[a-z],[A-Z],
[0-9],
[a-zA-Z0-9]
Zeichenklassen passen auf ein Zeichen, das in der Klasse enthalten ist.
[aeiou] passt auf a, e, i, o oder u, aber nicht auf x. Es können auch Bereiche angegeben werden,
[a-z] passt auf alle Kleinbuchstaben.
[a-zA-Z0-9] Bereiche können auch kombiniert werden.
Die Sonderzeichen verlieren in einer Klasse ihre Wirkung: Punkt, Stern, Fragezeichen (siehe oben: Metacharaktere)
[^abc],
[^a-z],
[^a-zA-Z0-9]
^ negiert die Zeichenklassen:
[^abc] passt auf jedes beliebige Zeichen, außer a, b und c.
(!! außerhalb der [ ] steht ^ für Eingabebeginn!! s.o.)
[a-z&&[^mn]] Verschachtelung und Ausschluss: a bis z außer m und n

Quantifizierer: Häufigkeit des Auftretens (außerhalb der Zeichenklassendefinition!)

RegEx Erläuterung
* Das vorangehende Element (ein einzelnes Zeichen, eine geklammerte Zeichenfolge oder eine Zeichenklasse)
darf beliebig oft vorkommen, auch keinmal.
+ Das vorangehende Element
muss mindestens einmal, darf aber auch öfter vorkommen.
? Das vorangehende Element darf
nicht oder genau einmal vorkommen, aber keinesfalls öfter.
{n} Das vorangehende Element
muss genau n-mal vorkommen.
{n,} Das vorangehende Element
muss mindestens n-mal vorkommen.
{n, m} Das vorangehende Element
muss mindestens n-mal aber höchstens m-mal vorkommen.
{,m} Das vorangehende Element
darf höchstens m-mal vorkommen. (geht bei wenigen Parsern)

Lazy / Greedy

Reguläre Ausdrücke mit den Quantifizierern * und + sind gierig (greedy): sie versuchen, so viel wie möglich Treffer zu erzielen und das Suchmuster über möglichst viele Zeichen auszudehnen. In der Regel ist dies jedoch nicht gewünscht: Bestimmte Zeichenklassen sollen sich nur so lange wiederholen, bis das erste Vorkommen der folgenden Zeichenklasse auftritt. Diese genügsamere Suchcharakteristik (lazy) lässt sich umstellen, in dem die lazy Varianten der Multiplizität genutzt werden (*? und +?): Das Fragezeichen kommt hier als Metacharakter erneut zum Zug:

Beispieltext greedy RegEx lazy RegEx
<kunden><kunde></kunde></kunden> <.*>
findet Gesamttext
<.*?>
findet nur <kunden>
Es gibt kein richtiges Leben im falschen E.+n
findet Gesamttext
E.+?n
findet Es gibt kein
dadada (da){1,}
findet dadada
(da){1,}?
findet drei mal da

Literale und Metacharaktere als Elementarbausteine

In Regulären Ausdrücken wird zwischen Literalen und Metacharakteren unterschieden: Literale repräsentieren das jeweilige Zeichen, Metacharaktere haben eine syntaktische Bedeutung. Alle Zeichen, die Metacharaktere repräsentieren, lassen sich über einen Backslash maskieren und repräsentieren so das eigentliche Zeichen (z.B. \[ für die eckige Klammer).

Zeichen Metacharakter
Bedeutung innerhalb
einer Zeichenklasse [ ]
Metacharakter
Bedeutung außerhalb
einer Zeichenklasse
. beliebiges Zeichen
^ Negierung
[^a]: kein a
Beginn der Eingabe
$ Ende der Eingabe
? 0-1-mal (Quantifizierer {0,1})
(? ...) leitet Direktiven ein
* *: 0-n-mal (Quantifizier {0,})
*?: lazy-Variante
+ 1-n-mal (Quantifizier {1,})
+?: lazy-Variante
| oder
[ Klassenbeginn
] Klassenende
{ } von - bis mal (Quantifizierer)
( ) Gruppierung
\ Maskierung Maskierung
/ Begrenzung der RegEx
- Bereichskennzeichnung

Immer dann, wenn für ein Zeichen in der obigen Tabelle eine Metacharakterbedeutung zugewiesen ist, muss das Zeichen maskiert werden, wenn es als Literal gesucht werden soll.

Übersicht vordefinierter Zeichenklassen

Vordefinierte Zeichenklassen

RegEx Erläuterung
. Der Punkt passt auf ein beliebiges Zeichen (bei vielen Parsern: bis auf Newline)
\d = [0-9]
\D = [^0-9]
digit: Vordefinierte Zeichenklassen für Ziffern. \d passt auf alle Ziffern, entspricht also genau [0-9]. (\D ist genau das Gegenteil und passt auf alles außer Ziffern.)
\t, \n, \r Tabulator, linefeed (Zeilenvorschub, LF), carriage return (Wagenrücklauf, CR)
*nix nutzen \n als Zeilenumbruch, Windows \r\n
\s = nicht \S whitespace: \s ist eine vordefinierte Klasse, die auf Whitespaces passt, also Leerzeichen, Zeilenumbruch, Tabulator usw. [\t\n\x08\x0c\r] (\S ist wieder das Gegenteil)
\w = [a-zA-Z_0-9]= nicht \W word: \w entspricht [a-zA-Z_0-9], ob Umlaute erkannt werden können, hängt von der Systemkonfiguration ab (\W ist die Negation davon)
\b = nicht \B Wortgrenze, also Tabulator, Leerzeichen
\xhh Zeichen mit Hexadezimalwert hh
\uhhhh
\N{EURO SIGN}
Unicodezeichen mit Hexcode hhhh bzw. in Unicode-Notation

Position und Grammatik (außerhalb der Zeichenklassendefinition!)

RegEx Erläuterung
^ start of string anchor: Anfang der Eingabe. ^ana passt in ananas, aber nicht in banane.
$ end of string anchor: Ende der Eingabe. ze$ passt in Katze, aber nicht in Katzen.
\A \Z Stringbeginn und -ende
\< \> Wortbeginn und -ende
\| Oder-Verknüpfung, entweder der Ausdruck links oder der Ausdruck rechts muss passen.
() Mit Klammern gruppierte und selektierte Reguläre Ausdrücke können in der Reihenfolge gezielt ausgegeben werden: Ausgabe im RegEx über \1 (erste Klammer) \2 usw… (parserabhängig auch $1 $2 usw.) oder per Java über den matcher.group(1)
(?<wert>) Selektierte Ausdrücke können auch mit einem Namen versehen werden (hier: wert ) und über diesen im Matcher (z.B. Java) gezielt angesprochen werden: (?<wert>[0-9.,]+)\\s*(?<waehrung>[A-Z]{3})
matcher.group("wert")
(?:) Ausdruck wird gruppiert, ist aber nicht selektiv ansprechbar. (geht bei wenigen Parsern)
(?=) Ausdruck: “Muss gefolgt werden von” Ausdruck vor der Klammer muss gefolgt werden von Ausdruck in der Klammer. Letzterer wird aber nicht in das Ergebnis geschrieben.
(?!) Ausdruck: “Darf nicht gefolgt werden von” (entsprechend wie oben)

POSIX-Zeichenklassen

RegEx Erläuterung
\p{Lower} ASCII Kleinbuchstabe: [a-z]
\p{Upper} ASCII Großbuchstabe: [A-Z]
\p{ASCII} ASCII Zeichen: [\x00-\x7f]
\p{Alpha} Buchstabe: [A-Za-z]
\p{Digit} Ziffer: \d
\p{Alnum} Buchstabe oder Ziffer

Unicode-Zeichenklassen

RegEx Erläuterung
\p{Lu} Unicode Großbuchstabe
\p{Ll} Unicode Kleinbuchstabe
\p{Sc} Unicode Währungssymbol
\p{Nl} Unicode Zahl oder Buchstabe

Flags

RegEx Erläuterung
(?i) Case-insensitive (ASCII)
(?iu) Case-insensitive (Unicode)
(?g) global match
(?m) Multi-line mode
(?s) Single-line / dotall mode

Beispiele zum Verständnis

  • CSBME findet exakt die Zeichenkette CSBME
  • ^CSBME$ findet eine Zeile, die ausschließlich aus der Zeichenkette CSBME besteht
  • (Hund|Katze|Maus) findet die Wörter Hund oder Katze oder Maus
  • [Hund|Katze|Maus] die Buchstaben HKMadenstuz sowie die Pipe
  • [0-9]{2}-[0-9]{2}-[0-9]{4} findet ein Datum der Notation 01-01-2000
  • suche: ([0-9]{2})-([0-9]{2})-([0-9]{4})
    ersetze: \1\.\2\.\3
    wandelt ein Datum von der Form 01/01/2000 in die Form 01.01.2000 um.

RegEx in Programmiersprachen nutzen:

Text suchen

Python bietet mit re eine Library, die das Handling für Reguläre Ausdrücke komfortablen übernimmt:

Text aufteilen

Text ersetzen

Teile eines Suchmusters ersetzen

Die im ersten String mit Klammern gruppierten Gruppenergebnisse können über \g<1> bis \g<n> der Reihe nach referenziert werden. \g<0> referenziert den gesamten gefundenen Ausdruck. Das führende r bei r'\g<1>...' setzt den String in den raw-Mode, auf diese Art müssen Schrägstriche nicht maskiert werden.

Mithilfe dieser Funktion können komplexe Suchen/Ersetzen-Aufgaben erledigt werden. Obiges Programm/Pattern gibt beispielsweise 030 neuer Text DASHIERNICHT kleinbuchstabenersetzen aus.

RegEx in Powershell und Bash

Mit RegEx über das Terminal nach Dateiinhalten suchen

Beispiel: in einer Datei befindet sich irgendwo ein ungewöhnliches Zeichen, weswegen die Datei nicht verarbeitet werden kann. Folgende Aufrufe mit regulären Ausdrücken finden die betreffenden Zeilen:

Mit RegEx über das Terminal Dateiinhalte verändern

In einer Textdatei soll bei allen Geldbeträgen, die mit EUR enden das Zeichen € gesetzt werden. Da im Text auch an anderen Stellen EUR steht, muss sichergestellt werden, dass nur bei den Geldbeträgen die Währungsbezeichnung ersetzt wird. Folgender Aufruf ändert das direkt in der Datei:

Noch kein Beispiel für die Powershell vorhanden

RegEx in SQL-Abfragen

PostgreSQL bietet mit den Funktionen regexp_instr() und regexp_like() Möglichkeiten, die Mustererkennung komfortabel durchzuführen. Weitere Infos dazu unter diesem Link.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Mustererkennung mit Regular Expressions (RegEx)” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/regex veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/regex/regex-basics. Stand: 13.12.2024.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: