Wie verwenden Sie Regex tatsächlich?

Regex, kurz für regulärer Ausdruck, wird häufig in Programmiersprachen zum Abgleichen verwendet Muster in Zeichenfolgen, Suchen und Ersetzen, Eingabeüberprüfung und Neuformatierung von Text. Das Erlernen der richtigen Verwendung von Regex kann das Arbeiten mit Text erheblich vereinfachen.

Regex-Syntax, erklärt

Regex hat den Ruf, Regex zu haben schreckliche Syntax, aber es ist viel einfacher zu schreiben als zu lesen. Hier ist beispielsweise ein allgemeiner regulärer Ausdruck für einen RFC 5322-kompatiblen E-Mail-Validator:

(?: [a-z0-9! # $% & amp; '* +/=? ^ _ `{|} ~ -] + (?: . [a-z0-9! # $% & amp;' * +/=? ^ _ `{|} ~ -] +) * |” (?: [ x01- x08 x0b x0c x0e- x1f x21 x23- x5b x5d- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) * “) @ (?: (?: [a-z0-9] (?: [a-z0-9 -] * [a -z0-9])? .) + [a-z0-9] (?: [a-z0-9 -] * [a-z0-9])? | [(? :(?: 25 [ 0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) .) {3} (?: 25 [0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9] | [a-z0-9 -] * [a-z0-9]: (?: [ X01 – x08 x0b x0c x0e- x1f x21- x5a x53- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) +) ])

Wenn es so aussieht, als hätte jemand sein Gesicht in die Tastatur geschlagen, sind Sie nicht allein. Aber unter der Haube programmiert all dieses Durcheinander tatsächlich eine Finite-State-Maschine. Diese Maschine läuft für jeden Charakter und tuckert mit und stimmt mit den Regeln überein, die Sie festgelegt haben. Viele Online-Tools rendern Eisenbahndiagramme und zeigen, wie Ihre Regex-Maschine funktioniert. Hier ist derselbe Regex in visueller Form:

Immer noch sehr verwirrend, aber es ist viel verständlicher. Es handelt sich um eine Maschine mit beweglichen Teilen, deren Regeln festlegen, wie alles zusammenpasst. Sie können sehen, wie jemand dies zusammengestellt hat; Es ist nicht nur eine große Textkugel.

Zunächst einmal: Verwenden Sie einen Regex-Debugger

Bevor wir beginnen, es sei denn, Ihr Regex ist besonders kurz oder Sie sind besonders kompetent sollten Sie beim Schreiben und Testen einen Online-Debugger verwenden. Dies erleichtert das Verständnis der Syntax erheblich. Wir empfehlen Regex101 und RegExr, die beide Tests und eine integrierte Syntaxreferenz bieten.

Wie funktioniert Regex?

Konzentrieren wir uns zunächst auf etwas viel Einfacheres. Dies ist ein Diagramm von Regulex für einen sehr kurzen (und definitiv nicht RFC 5322-kompatiblen) E-Mail-passenden Regex:

Die Regex-Engine startet links und fährt die Zeilen entlang, wobei die Zeichen dabei übereinstimmen. Gruppe 1 stimmt mit jedem Zeichen außer einem Zeilenumbruch überein und stimmt weiterhin mit Zeichen überein, bis der nächste Block eine Übereinstimmung findet. In diesem Fall wird es angehalten, wenn ein @ -Symbol erreicht wird. Dies bedeutet, dass Gruppe 1 den Namen der E-Mail-Adresse und alles, was danach übereinstimmt, mit der Domain erfasst.

Der Regex, der Gruppe 1 in unserem E-Mail-Beispiel definiert ist:

(. +)

Die Klammern definieren eine Erfassungsgruppe, die die Regex-Engine anweist, den Inhalt der Übereinstimmung dieser Gruppe in eine spezielle Variable aufzunehmen. Wenn Sie einen Regex für eine Zeichenfolge ausführen, ist die Standardrückgabe die gesamte Übereinstimmung (in diesem Fall die gesamte E-Mail). Es wird jedoch auch jede Erfassungsgruppe zurückgegeben, wodurch dieser Regex zum Abrufen von Namen aus E-Mails nützlich ist.

Der Punkt ist das Symbol für “Alle Zeichen außer Zeilenumbruch”. Dies stimmt mit allem in einer Zeile überein. Wenn Sie also diese E-Mail an Regex übergeben, erhalten Sie eine Adresse wie:

% $ # ^ & amp;% * #% $ # ^ @ gmail.com

Es würde mit% $ # ^ & amp übereinstimmen ;% * #% $ # ^ Als Name, obwohl das lächerlich ist.

Das Pluszeichen (+) ist eine Kontrollstruktur, die bedeutet, dass das vorhergehende Zeichen oder die vorhergehende Gruppe mindestens einmal übereinstimmt. & # 8221; Es stellt sicher, dass der gesamte Name übereinstimmt und nicht nur das erste Zeichen. Dies ist es, was die Schleife im Eisenbahndiagramm erzeugt.

Der Rest des Regex ist ziemlich einfach zu entschlüsseln:

(. +) @ (. + .. +)

Die erste Gruppe stoppt, wenn sie auf das @ -Symbol trifft. Die nächste Gruppe beginnt dann, die wieder mehreren Zeichen entspricht, bis sie ein Punktzeichen erreicht.

Da Zeichen wie Punkte, Klammern und Schrägstriche als Teil der Syntax in Regrex verwendet werden, müssen Sie sie jederzeit mit einem Backslash versehen, wenn Sie diese Zeichen abgleichen möchten. In diesem Beispiel, um mit dem Zeitraum übereinzustimmen, den wir schreiben . und der Parser behandelt es als ein Symbol, das bedeutet, dass es mit einem Punkt übereinstimmt.

Zeichenübereinstimmung

If Wenn Sie Regex-Zeichen ohne Kontrolle haben, geht die Regex-Engine davon aus, dass diese Zeichen einen passenden Block bilden. Beispiel: Der Regex:

he + llo

stimmt mit dem Wort “Hallo” überein. mit einer beliebigen Anzahl von e. Alle anderen Zeichen müssen maskiert werden, damit sie ordnungsgemäß funktionieren.

Regex hat auch Zeichenklassen, die als Abkürzung für eine Reihe von Zeichen dienen. Diese können je nach Regex-Implementierung variieren, diese wenigen sind jedoch Standard:

  • . & # 8211; stimmt mit allem überein, außer mit Zeilenumbruch.
  • w & # 8211; stimmt mit jedem & # 8220; Wort & # 8221; Zeichen, einschließlich Ziffern und Unterstrichen.
  • d & # 8211; stimmt mit Zahlen überein.
  • b & # 8211; stimmt mit Leerzeichen überein (d. h. Leerzeichen, Tabulator, Zeilenumbruch).

Diese drei haben alle Gegenstücke in Großbuchstaben, die ihre Funktion umkehren. Zum Beispiel stimmt D mit allem überein, was keine Zahl ist.

Regex hat auch eine Zeichensatzübereinstimmung. Zum Beispiel:

[abc]

Entspricht entweder a, b oder c. Dies fungiert als ein Block, und die eckigen Klammern sind nur Kontrollstrukturen. Alternativ können Sie einen Zeichenbereich angeben:

[ac]

Oder negieren Sie den Satz, der mit jedem Zeichen übereinstimmt, das nicht im Satz enthalten ist:

[^ ac]

Quantifizierer

Quantifizierer sind ein wichtiger Bestandteil von Regex. Mit ihnen können Sie Zeichenfolgen abgleichen, bei denen Sie das genaue Format nicht kennen, aber Sie haben eine ziemlich gute Idee.

Der Operator + aus dem E-Mail-Beispiel ist ein Quantifizierer, insbesondere der & # 8220; oder mehr & # 8221; Quantor. Wenn wir nicht wissen, wie lang eine bestimmte Zeichenfolge ist, aber wir wissen, dass sie aus alphanumerischen Zeichen besteht (und nicht leer ist), können wir schreiben:

w +

Zusätzlich zu + gibt es auch:

  • Der Operator *, der mit & # 8220; null oder mehr übereinstimmt. & # 8221; Im Wesentlichen dasselbe wie +, außer dass die Option besteht, keine Übereinstimmung zu finden.
  • Das? Operator, der mit “Null” oder “Eins” übereinstimmt. Dies hat zur Folge, dass ein Zeichen optional wird. Entweder ist es dort oder es ist nicht vorhanden, und es wird nicht mehr als einmal übereinstimmen.
  • Numerische Quantifizierer. Dies kann eine einzelne Zahl wie {3} sein, was bedeutet, dass & # 8220; genau 3 Mal & # 8221; oder ein Bereich wie {3-6}. Sie können die zweite Zahl weglassen, um sie unbegrenzt zu machen. Zum Beispiel bedeutet {3,} & # 8220; 3 oder mehr Mal & # 8221;. Seltsamerweise können Sie die erste Nummer nicht auslassen. Wenn Sie also 3 oder weniger Mal möchten, können Sie & # 8221; Sie müssen einen Bereich verwenden.

Gierige und faule Quantifizierer

Unter der Haube befindet sich die * Und + Operatoren sind gierig. Es stimmt so gut wie möglich überein und gibt zurück, was zum Starten des nächsten Blocks erforderlich ist. Dies kann ein massives Problem sein.

Hier ein Beispiel: Angenommen, Sie versuchen, HTML oder etwas anderes mit schließenden Klammern abzugleichen. Ihr Eingabetext lautet:

& lt; div & gt; Hallo Welt & lt;/div & gt;

Und Sie möchten alles in Klammern abgleichen. Sie können Folgendes schreiben:

& lt ;. * & Gt;

Dies ist die richtige Idee, schlägt jedoch aus einem entscheidenden Grund fehl: Die Regex-Engine entspricht “div & gt; Hello World & lt;/div & gt; & # 8221; für die Sequenz. * und dann zurückverfolgen, bis der nächste Block mit einer schließenden Klammer (& gt;) übereinstimmt. Sie würden erwarten, dass der Backtrack nur mit & # 8220; div & # 8220; übereinstimmt und dann erneut wiederholt wird, um mit dem schließenden Div übereinzustimmen. Der Backtracker läuft jedoch vom Ende des Strings ab und stoppt an der Endklammer, die schließlich mit allem in den Klammern übereinstimmt.

Die Lösung besteht darin, unseren Quantifizierer faul zu machen, was bedeutet, dass er als übereinstimmt wenige Zeichen wie möglich. Unter der Haube wird dies tatsächlich nur einem Zeichen entsprechen und dann erweitert, um den Raum bis zur nächsten Blockübereinstimmung zu füllen, was es bei großen Regex-Operationen viel leistungsfähiger macht.

Um einen Quantifizierer faul zu machen, wird ein Fragezeichen direkt nach dem Quantifizierer hinzugefügt. Das ist ein bisschen verwirrend, weil? ist bereits ein Quantifizierer (und ist standardmäßig gierig). In unserem HTML-Beispiel wird der Regex mit diesem einfachen Zusatz behoben:

& lt ;. *? & Gt;

Der Lazy-Operator kann an jeden Quantifizierer angeheftet werden, einschließlich +?, {0,3}? und sogar ??. Obwohl der letzte keine Wirkung hat; Da Sie ohnehin mit null oder einem Zeichen übereinstimmen, gibt es keinen Raum zum Erweitern.

Gruppierung und Lookarounds

< p>Gruppen in Regex haben viele Zwecke. Grundsätzlich verbinden sie mehrere Token zu einem Block. Sie können beispielsweise eine Gruppe erstellen und dann einen Quantifizierer für die gesamte Gruppe verwenden:

ba (na) +

Hiermit wird die wiederholte & # 8220; na & # 8221; passend zu den Ausdrücken Banane und Banananana und so weiter. Ohne die Gruppe würde die Regex-Engine nur immer wieder mit dem Endzeichen übereinstimmen.

Dieser Gruppentyp mit zwei einfachen Klammern wird als Erfassungsgruppe bezeichnet und in die Ausgabe aufgenommen:

Wenn Sie dies vermeiden möchten, gruppieren Sie Token einfach Aus Ausführungsgründen können Sie zusammen eine nicht erfassende Gruppe verwenden:

ba (?: na)

Das Fragezeichen (ein reserviertes Zeichen) definiert eine nicht standardmäßige Gruppe, und das folgende Zeichen definiert, um welche Art von Gruppe es sich handelt. Das Starten von Gruppen mit einem Fragezeichen ist ideal, da Sie sie sonst ohne guten Grund maskieren müssen, wenn Sie Semikolons in einer Gruppe abgleichen möchten. Sie müssen jedoch in Regex immer vor Fragezeichen stehen.

Sie können Ihre Gruppen auch bequem benennen, wenn Sie mit der Ausgabe arbeiten:

(? 'Group')

Sie können referenzieren diese in Ihrem Regex, wodurch sie ähnlich wie Variablen funktionieren. Sie können nicht benannte Gruppen mit dem Token 1 referenzieren, dies reicht jedoch nur bis zu 7. Danach müssen Sie mit dem Benennen von Gruppen beginnen. Die Syntax zum Verweisen auf benannte Gruppen lautet:

k {group}

Dies verweist auf die Ergebnisse der benannten Gruppe, die dynamisch sein können. Im Wesentlichen wird überprüft, ob die Gruppe mehrmals auftritt, ohne sich jedoch um die Position zu kümmern. Dies kann beispielsweise verwendet werden, um den gesamten Text zwischen drei identischen Wörtern abzugleichen:

In der Gruppenklasse finden Sie den größten Teil der Kontrollstruktur von Regex, einschließlich Lookaheads. Lookaheads stellen sicher, dass ein Ausdruck übereinstimmen muss, ihn jedoch nicht in das Ergebnis einbezieht. In gewisser Weise ähnelt es einer if-Anweisung und stimmt nicht überein, wenn es false zurückgibt.

Die Syntax für einen positiven Lookahead lautet (? =). Hier ein Beispiel:

Dies stimmt sehr genau mit dem Namensteil einer E-Mail-Adresse überein, indem die Ausführung am teilenden @ gestoppt wird. Lookaheads verbrauchen keine Zeichen. Wenn Sie also nach dem Erfolg eines Lookaheads weiterlaufen möchten, können Sie dennoch mit dem im Lookahead verwendeten Zeichen übereinstimmen.

Neben positiven Lookaheads gibt es auch:

  • (?!) & # 8211; Negative Lookaheads, die sicherstellen, dass ein Ausdruck nicht übereinstimmt.
  • (? & lt; =) & # 8211; Positive Lookbehinds, die aufgrund technischer Einschränkungen nicht überall unterstützt werden. Diese werden vor dem Ausdruck platziert, mit dem Sie übereinstimmen möchten, und sie müssen eine feste Breite haben (dh keine Quantifizierer außer {number}). In diesem Beispiel können Sie (? & Lt; = @) w + . W + verwenden, um eine Übereinstimmung zu erzielen der Domain-Teil der E-Mail.
  • (? & lt;!) & # 8211; Negative Lookbehinds, die mit positiven Lookbehinds identisch sind, jedoch negiert werden.

Unterschiede zwischen Regex-Motoren

Nicht alle Regex-Motoren sind gleich. Die meisten Regex-Motoren folgen keinem bestimmten Standard, und einige ändern die Einstellungen ein wenig Einige Funktionen, die in einer Sprache funktionieren, funktionieren möglicherweise nicht in einer anderen.

Beispielsweise unterstützen die für macOS und FreeBSD kompilierten Versionen von sed die Verwendung von t zur Darstellung eines Tabulatorzeichens nicht. Sie müssen ein Tabulatorzeichen manuell kopieren und in das Terminal einfügen, um einen Tabulator in der Befehlszeile sed zu verwenden.

Der größte Teil dieses Tutorials ist mit PCRE kompatibel, der Standard-Regex-Engine für PHP. Die Regex-Engine von JavaScript ist jedoch anders. Sie unterstützt keine benannten Erfassungsgruppen mit Anführungszeichen (in Klammern) und kann unter anderem keine Rekursion durchführen. Selbst PCRE ist nicht vollständig mit verschiedenen Versionen kompatibel und weist viele Unterschiede zu Perl-Regex auf.

Es gibt zu viele geringfügige Unterschiede, um sie hier aufzulisten. Sie können diese Referenztabelle daher verwenden, um die Unterschiede zu vergleichen zwischen mehreren Regex-Motoren. Mit Regex-Debuggern wie Regex101 können Sie auch Regex-Engines wechseln. Stellen Sie daher sicher, dass Sie das Debuggen mit der richtigen Engine durchführen.

So führen Sie Regex aus

Wir haben den passenden Teil der regulären Ausdrücke besprochen, der den größten Teil dessen ausmacht, was einen Regex ausmacht. Wenn Sie Ihren Regex jedoch tatsächlich ausführen möchten, müssen Sie ihn zu einem vollständigen regulären Ausdruck formen.

Dies hat normalerweise das Format:

/match/g

Alles Innerhalb der Schrägstriche ist unser Match. Das g ist ein Modusmodifikator. In diesem Fall weist es den Motor an, nicht zu laufen, nachdem er die erste Übereinstimmung gefunden hat. Um Regex zu finden und zu ersetzen, müssen Sie es häufig wie folgt formatieren:

/find/replace/g

Dies ersetzt die gesamte Datei. Beim Ersetzen können Sie Verweise auf Erfassungsgruppen verwenden, wodurch Regex sehr gut Text formatieren kann. Beispielsweise stimmt dieser Regex mit allen HTML-Tags überein und ersetzt die Standardklammern durch eckige Klammern:

/<(.+?)>/[1‹/g

Wenn dies ausgeführt wird, wird die Engine ausgeführt Übereinstimmung & lt; div & gt; und & lt;/div & gt;, damit Sie diesen Text (und nur diesen Text) ersetzen können. Wie Sie sehen können, ist der innere HTML-Code nicht betroffen:

Dies macht Regex sehr nützlich, um Text zu finden und zu ersetzen. Das dazu erforderliche Befehlszeilenprogramm ist sed, das das Grundformat verwendet:

sed '/find/replace/g' file & gt; Datei

​​Dies wird in einer Datei ausgeführt und an STDOUT ausgegeben. Sie müssen es an sich selbst weiterleiten (wie hier gezeigt), um die Datei auf der Festplatte tatsächlich zu ersetzen.

Regex wird auch in vielen Texteditoren unterstützt und kann Ihren Workflow beim Stapeln erheblich beschleunigen Operationen. In Vim, Atom und VS Code ist Regex zum Suchen und Ersetzen integriert.

Natürlich kann Regex auch programmgesteuert verwendet werden und ist normalerweise in vielen Sprachen integriert. Die genaue Implementierung hängt von der Sprache ab. Sie müssen daher die Dokumentation Ihrer Sprache konsultieren.

In JavaScript kann Regex beispielsweise buchstäblich oder dynamisch mit dem globalen RegExp erstellt werden Objekt:

var re = new RegExp ('abc')

Dies kann direkt durch Aufrufen der .exec () -Methode des neu erstellten Regex-Objekts oder mithilfe von .replace (), .match verwendet werden () und .matchAll () Methoden für Zeichenfolgen.


Posted

in

by

Tags: