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 23.05.2023)
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.

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 zwischenA
undZ
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.
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 | Beginn der Eingabe |
$ |
Ende der Eingabe | |
? |
0-1mal Quantifizierer(? ...) leitet Direktiven ein |
|
* |
0-n mal (Quantifizier) | |
+ |
1-n mal (Quantifizier) | |
| |
oder | |
[ |
Klassenbeginn | |
] |
Klassenende | |
{ } |
von - bis mal (Quantifizierer) | |
( ) |
Gruppierung | |
\ |
Maskierung | Maskierung |
/ |
Begrenzung der RegEx | |
- |
Bereichs-kennzeichnung |
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.
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 |
<.*?> <br/ 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 |
Übersicht vordefinierter Zeichenklassen
Definition von Zeichenklassen
RegEx | Erläuterung |
---|---|
[abc] ,[a-z] ,[A-Z] ,[0-9] , [a-zA-Z0-9] |
Zeichenklassen passen auf ein Zeichen, das in der Klasse enthalten ist.[abc] passt auf a, b oder c, aber nicht auf x. Sie können auch Bereiche angeben,[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 |
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 = ^\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] = ^\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 = ^\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) |
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) |
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 BuchstabenHKMadenstuz
sowie die Pipe[0-9]{2}-[0-9]{2}-[0-9]{4}
findet ein Datum der Notation01-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
Die Klasse String bietet mir der Methode matches()
die Möglichkeit, unmittelbar zu prüfen, ob ein bestimmtes Muster vorliegt:
if (!text.matches("[0-9]*")) {
// Findet Werte, die keine (!) Zahlen sind
}
if (text.matches("[0-9A-Za-z]*")) {
// Findet Zahlen, Buchstaben, ohne Sonderzeichen
}
Teilstrings tauschen
Man kann aber nicht nur prüfen, ob bestimmte Muster vorkommen, man kann auch konkrete Muster ersetzen (mit der String-Methode replace()
):
text = "Alle 14 Zahlenziffern, die in diesen 392 Buchstaben an 4 verschiedenen Stellen stehen sollen 1 mal ersetzt werden.";
text = text.replaceAll("[0-9]","X");
System.out.println(text);
Komplexere Pattern mit Matches und LookingAt
Komplexere als die oben genannten Beispiele lassen sich über die Klassen Pattern und Matcher realisieren, die für ein Muster und einen Prüfer stehen. Zunächst die bekannte Darstellung (Zeile „boolean …“) , darunter dasselbe Problem mit Pattern und Matcher umgesetzt.
String str = "X";
String regex = "[A-Z]";
// Test auf Großbuchstabe (ASCII)
boolean matches = str.matches(regex);
// Alternative: pre-compiled pattern
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
matches = matcher.matches();
// LookingAt
matcher = pattern.matcher("Hallo");
matches = matcher.matches(); // false
startsWith = matcher.lookingAt(); // true
Group
Die Vorteile von Pattern/Matcher lassen sich insbesondere ausspielen, wenn mehrere Teilbereiche von einer Zeichenkette gesucht sind. Die eingeklammerten Bereiche der RegEx können gezielt (über matcher.group()
) angesprochen und ausgegeben werden:
String str = "17-25";
String regex = "(\\d+)-(\\d+)"; // Backslash im Sourcecode verdoppeln!
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
if (matcher.matches()) {
System.out.printf("Von %s bis %s\n",
matcher.group(1), matcher.group(2));
}
Split
Regular Expressions können auch genutzt werden, um Zeichenketten in (mehrere) Teilzeichenketten zu teilen:
String str = "1,2, 3, 4, 5,6";
String regex = "\\W+";
String[] elements = str.split(regex); // Aufteilen an Wortgrenzen
// Alternative: pre-compiled pattern
Pattern pattern = Pattern.compile(regex);
elements = pattern.split(str);
Find
Sofern ein Suchmuster öfters gefunden wurden, können alle Ergebnisse mit Hilfe einer while-Schleife durchlaufen werden:
String str = "Alle RDBMS benutzen SQL";
String regex = "\\p{Lu}{2,}";
// Finde all Wörter mit mindestens zwei
// Großbuchstaben (Unicode)
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
System.out.println(str.substring(
matcher.start(), matcher.end()));
}
Replace
Auch das eingangs erwähnte tauschen von Zeichenketten kann für komplexere Probleme mit Pattern/Matcher umgesetzt werden:
String str = "value with spaces";
String regex = "\\s+";
String replacement = "+"; // Ersetze whitespace mit +
String no_spaces = str.replaceAll(regex, replacement);
// Alternative: pre-compiled pattern
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
no_spaces = matcher.replaceAll(replacement);
// Ersetze ${name} mit System.getProperty(name)
String str = "Hallo ${user.name}!";
String regex = "\\$\\{([a-z.]+)\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, System.getProperty(matcher.group(1)));
}
matcher.appendTail(sb);
System.out.println(sb.toString());
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.
Links und weitere Informationen
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: 23.05.2023.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]