Reguläre Ausdrücke: Die Abfrage der Meiers Regex zum Suchen und Ersetzen nutzen

Autor / Redakteur: Mirco Lang / Stephan Augsten |

Regular Expressions gehören zu den mächtigsten Werkzeugen überhaupt. Und das gilt nicht nur für Entwickler oder Administratoren, auch Büroangestellte und Ottonormalverbraucher können immens davon profitieren. Wenn das nur nicht immer so kompliziert aussähe. Ein Crash-Kurs am Beispiel verschiedener Meier-Schreibweisen.

Anbieter zum Thema

Bei Regex101 lassen sich wunderbar Ausdrücke in verschiedenen Dialekten testen.
Bei Regex101 lassen sich wunderbar Ausdrücke in verschiedenen Dialekten testen.
(Bild: Lang / Regex101)

Ein regulärer Ausdruck, kurz Regex von Regular Expression, ist zugegebenermaßen auf den ersten Blick nicht unbedingt eine Schönheit – könnte man meinen. Eingeweihte allerdings dürften eine Regex nicht nur verstehen, sondern auch ihren ästhetischen Wert erkennen.

Eleganz ist ein Wort, das eine gute Regular Expression perfekt beschreibt. Nach der Lektüre dieses Artikels wird Ihnen die folgende Zeile jedenfalls nicht viel komplizierter vorkommen als der omnipräsente String „Hallo Welt”:

(Mirca) ((?<!-)\b[Mm]{1}[ae]{1}[iy]{1}er\b(?!-))

Aber bis zu diesem Konstrukt, das deutlich komplizierter aussieht als es wirklich ist, fehlen noch ein paar Bausteine. Vor allem natürlich die Frage: Was macht ein regulärer Ausdruck überhaupt?

Pattern Matching mit Abweichungen

Bei Regex geht es um Mustererkennung, wobei meist selbst im Deutschen eher vom Pattern Matching gesprochen wird. Ein Muster ist hier eine bestimmte Kette von Zeichen – mit einigen Unsicherheiten. Das Wort „Meier“ in einem Text oder Tausenden von Texten zu finden, ist kein Problem. Alle unterschiedlichen Schreibweisen von Meier zu finden und dabei Dinge wie „Schmidt-Meier“ oder „Herumeiern” auszuschließen oder nur „Mirca“ Meier zu matchen, aber durchaus.

Reguläre Ausdrücke lösen genau dieses Problem. Es ist eine Möglichkeit, in einem Text so ziemlich alles per Software zu finden, was auch ein Mensch finden kann. Und nach dem Finden kann man es natürlich auch verarbeiten. So ließen sich zum Beispiel alle wie auch immer geschriebenen Meiers in einer Adressdatenbank nach Schmidt umbenennen oder auch E-Mail-Adressen aus riesigen Textbeständen extrahieren.

Wenn ein Mediacenter wie Kodi eine Medienbibliothek aufbaut und dazu aus den vorhandenen Dateien Einträge wie „Serie XY Staffel 02 Episode 22“ erstellt, läuft auch das über Regular Expressions, die Dateinamen (im Wesentlichen) nach Mustern wie „s02_e22“ durchsuchen. Dort werden dann 2 und 22 für Staffel und Episode extrahiert und für die Suche in Online-Filmdatenbanken genutzt.

Regex-Abfragen kommen im Hintergrund viel häufiger vor als man vielleicht meinen möchte. Gerade Programmierer sollten sie aber auch kennen, um am eigenen Code selbst zu arbeiten. Wie oft setzt man am Anfang eines Projekts unbedacht wenig aussagekräftige Variablen oder nutzt kryptischen Spaghetticode? Mit Regexen lässt sich Quellcode auch wunderbar glattpolieren.

Bevor es nun an die Praxis geht, hier das wichtigste Werkzeug für den Regex-Einstieg: Auf der Seite Regex101.com können Sie Ihre Ausdrücke sowie zu durchsuchenden Text eingeben und sich live die Treffer anschauen. Die Seite nimmt Ihre Regex dabei komplett auseinander und erklärt jeden einzelnen Teil. Das ist super zum Lernen, hilft aber auch bei der Fehlersuche in komplexen Ausdrücken.

Reguläre Ausdrücke entwickeln – die Meiers finden

Reguläre Ausdrücke bestehen aus unterschiedlichen Bestandteilen. Definitionen von Zeichen (a und/oder b), Quantifizierern (*, {min, max},? etc.), Platzfreihaltern (. für beliebige Zeichen), Steuerzeichen (\. für das Zeichen „Punkt“) und Gemeinheiten, etwa dem Negative Lookbehind ((?<!)).

Um mal mit einem ganz einfachen Beispiel anzufangen: Angenommen, Sie haben eine heiratswütige Anverwandte, die mal „Mirca Schmidt Meier“, mal „Mirca Müller Meier“ und mal „Mirca Fischer Meier“ geheißen hat. Wenn Sie nun alle Versionen finden wollen:

Mirca.*Meier

Der Platzhalter . steht für ein beliebiges Zeichen, der Quantifizierer * für eine beliebige Menge; es wird also alles gefunden, was mit Mirca anfängt und mit Meier aufhört – auch „123Mirca456Meier789“ würde gefunden. Wenn nun nicht beliebig viele beliebige Zeichen zwischen Mirca und Meier stehen dürfen, sondern nur drei bis fünf Zahlzeichen:

Mirca[0-9]{3,5}Meier

In eckigen Klammern werden Zeichenklassen angegeben, häufig zum Beispiel [A-Za-z0-9] für alle Buchstaben und Ziffern – und damit es nicht zu einfach wird, gibt es für viele Zeichenklassen auch Abkürzungen, beispielsweise \d für Ziffern (digits), wobei der Backslash grundsätzlich als Escape-Zeichen genutzt wird.

Wollen Sie also ein Regex-Steuerzeichen wie den Punkt oder eine Klammer wörtlich finden, müssen Sie jeweils \. beziehungsweise \( nutzen. Der Backslash ist zum Lesen von Regexen das vielleicht wichtigste Zeichen. Quantifizierer in Form von Bereichen werden in geschweiften Klammern angegeben. Erraten Sie, was ...

Mirca\.{1}Meier

... bedeutet? Gefunden würde hier ausschließlich „Mirca.Meier“, weil genau ein wörtlicher Punkt zwischen Mirca und Meier stehen darf. Das {1} ließe sich auch als ? schreiben – beides überflüssig, da ein einzelnes Vorkommen der Standard ist. „Mirca...Meier“ würde man entsprechend via \.* finden.

Damit ist der grundsätzliche Aufbau einer Regex klar und es darf etwas praktischer werden: Nehmen Sie an, Sie möchten jede Mirca Meier finden und das Meier – auch in anderen Schreibweisen – gegen Wolf austauschen. Dann haben Sie zwei Aufgaben: Meier-Schreibweisen definieren und dann nur das Meier von Mirca Meier zu finden und auszutauschen. Nicht aber das Meier von etwa Peter Meier oder Jochen Meier oder Mirca Meier-Schmidt.

Ein simples Ge-Meier:

[Mm]{1}[ae]{1}[iy]{1}er\b

Die Regex sagt ganz kurz: Zunächst muss ein „M“ oder „m“ kommen – genau ein Mal. Dann kommt ebenfalls genau ein „a“ oder „e“, anschließend ein „y“ oder „i“ und zum Schluss das er. Damit matcht der Ausdruck Meier, Maier, Meyer und Mayer jeweils klein und groß geschrieben. Aber es würde auch 123Meier456 gefunden.

Sie können den Ausdruck aber auf ein Wort begrenzen: Über die Wort-Abgrenzung \b (für boundary) werden Zeichen verlangt, die üblicherweise ein Wort abgrenzen (etwa Leerzeichen):

\b[Mm]{1}[ae]{1}[iy]{1}er\b

Leider meinen Regexe aber, dass auch der Bindestrich ein Wortbegrenzer ist – somit würden auch Peter Meier-Schmidt und Jochen Schmidt-Meier gefunden. Also muss der Bindestrich ebenfalls verboten werden – und jetzt wird es endgültig kryptisch:

(?<!-)\b[Mm]{1}[ae]{1}[iy]{1}er\b(?!-)

Neu sind hier ganz vorne (?<!-) und ganz hinten (?!-). Das Konstrukt (?<!) nennt sich Negative Lookbehind und besagt, dass die folgende Regex-Definition nicht matcht, wenn das angegebene Zeichen davor steht – mit (?<!-) wird also der Bindestrich vor Meier adressiert. Ist er vorhanden, handelt es sich um kein gültiges Match. (?!) nennt sich Negative Lookahead und erledigt als (?!-) dieselbe Aufgabe für einen Bindestrich nach Meier.

Negative wie positive Lookaheads und Lookbehinds machen reguläre Ausdrücke deutlich komplexer, weil die eigentlich (fast) komplett serielle Abarbeitung hier außer Kraft gesetzt wird. Sie eigenen sich wunderbar, um nicht nur eine bestimmte Syntax zu finden, sondern diese auf einen bestimmten Kontext zu beschränken.

Auch für die angedachte Aufgabe braucht es noch etwas Kontext: Ziel der ganzen Meierei soll ja sein, Mirca Meier/Meyer/Maier/… nach Mirca Wolf umzubenennen. Die Einschränkung auf Mirca ist einfach, es wird einfach vor die Meier-Definition gesetzt (was sich wiederum auch mit einem positiven Lookbehind lösen ließe):

Mirca (?<!-)\b[Mm]{1}[ae]{1}[iy]{1}er\b(?!-)

Nun ist der Match „Mirca Meier“, von der Umbenennung betroffen ist aber nur das Meier. Also kommen noch ein paar Klammern hinzu: Über runde Klammern werden Treffergruppen gesetzt, die später einzeln ansprechbar sind:

(Mirca) ((?<!-)\b[Mm]{1}[ae]{1}[iy]{1}er\b(?!-))

Mirca und Meier sind jetzt jeweils in () gepackt. Die Definition ist damit fertig: Gefunden werden alle Schreibweisen von „Mirca Meier“; Mirca landet in der Variablen $1 und das Meier in der Variablen $2.

Um das Ganze nun in der Praxis zu nutzen, gibt es viele verschiedene Möglichkeiten, beispielsweise in Perl:

perl -pi -e 's/(Mirca) ((?<!-)\b[Mm]{1}[ae]{1}[iy]{1}er\b(?!-))/$1 Wolf/g' ./test.txt

Damit würde nun jedes Vorkommen von Mirca Meier in der Datei test.txt durch Mirca ($1) Wolf ersetzt.

Zur Regex selbst sind noch zwei Elemente gekommen, die Sie häufig sehen werden: Bei der Schreibweise 's/Original/Ersatz/g' steht das s für substitute (ersetzen) und das abschließende g wendet die Regex auf das gesamte Dokument an und stoppt nicht gleich nach dem ersten Treffer.

Sie sehen, Regexe sind im Grunde ganz liebe Tierchen, die einfach nur Stück für Stück abstrakt nachbilden, wie ein Stück Text aussehen muss. Nur die Lookaheads/Lookbehinds sind tatsächlich kleine böse Stacheln: Sie ruinieren das (fast) rein sequenzielle Abarbeiten, haben eine wirklich unschöne Syntax und funktionieren nicht überall.

Generell gibt es einige Unterschiede zwischen den Regex-Dialekten. Mal sind das kleine Unterschiede bei der Syntax, mal gravierende, so kann nämlich beispielsweise das wunderbare sed überhaupt nicht mit Lookaheads und Lookbehinds umgehen – und viele andere Programme können es auch nicht.

Tipp: Kopieren Sie am besten die Meier-Regex in Regex101.com und schauen Sie sich die einzelnen Bestandteile nochmal genauer an. Anschließend können Sie immer weiter verfeinern, bis Sie das Meier-Konzept auch auf, sagen wir mal, URLs anwenden können – das ist dann ein ganz klein wenig anspruchsvoller. Eine von vielen Lösungen:

(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)

Und auch dieses Gewirr ist längst nicht perfekt oder für jede Situation geeignet.

(ID:45988574)