Warum funktioniert mein regulärer Ausdruck in X, aber nicht in Y?

76

Ich habe einen regulären Ausdruck geschrieben, der in einem bestimmten Programm gut funktioniert (grep, sed, awk, perl, python, rubin, ksh, bash, zsh, find, emacs, vi, vim, gedit, ...). Aber wenn ich es in einem anderen Programm (oder in einer anderen Unix-Variante) verwende, hört es auf zu passen. Warum?

Gilles
quelle

Antworten:

102

Leider haben verschiedene Tools aus historischen Gründen eine leicht unterschiedliche Syntax für reguläre Ausdrücke, und einige Implementierungen weisen manchmal Erweiterungen auf, die von anderen Tools nicht unterstützt werden. Obwohl es eine Gemeinsamkeit gibt, scheint es, dass jeder Werkzeugschreiber einige unterschiedliche Entscheidungen getroffen hat.

Wenn Sie einen regulären Ausdruck haben, der in einem Tool funktioniert, müssen Sie ihn möglicherweise ändern, damit er in einem anderen Tool funktioniert. Die Hauptunterschiede zwischen gängigen Tools sind:

  • ob die Operatoren +?|(){}einen Backslash benötigen;
  • Welche Erweiterungen werden über die Grundlagen hinaus .[]*^$und in der Regel unterstützt?+?|()

In dieser Antwort liste ich die wichtigsten Standards auf . Weitere Informationen finden Sie in der Dokumentation der von Ihnen verwendeten Tools.

Der Vergleich von regulären Ausdrucksmodulen in Wikipedia enthält eine Tabelle mit den Funktionen, die von gängigen Implementierungen unterstützt werden.

Grundlegende reguläre Ausdrücke (BRE)

Grundlegende reguläre Ausdrücke werden durch den POSIX-Standard codiert . Es ist die Syntax von grep, sedund vi. Diese Syntax bietet die folgenden Funktionen:

  • ^und $stimmen nur am Anfang und Ende einer Zeile überein.
  • . Entspricht einem beliebigen Zeichen (oder einem beliebigen Zeichen außer einem Zeilenumbruch).
  • […]Entspricht einem beliebigen Zeichen in Klammern (Zeichensatz). Wenn das erste Zeichen nach der öffnenden Klammer ein ist ^, werden stattdessen die nicht aufgelisteten Zeichen abgeglichen. Um ein einzuschließen ], setzen Sie es sofort nach dem Öffnen [(oder danach, [^wenn es eine negative Menge ist). Wenn -zwischen zwei Zeichen liegt, gibt dies einen Bereich an. Um ein Literal einzuschließen -, setzen Sie es an die Stelle, an der es nicht als Bereich analysiert werden kann.
  • Backslash vor einem der ^$.*\[Anführungszeichen des nächsten Zeichens.
  • * entspricht dem vorhergehenden Zeichen oder Unterausdruck 0, 1 oder mehrmals.
  • \(…\)ist eine syntaktische Gruppe zur Verwendung mit dem *Operator oder für Rückverweise und \DIGITErsetzungen.
  • Rückreferenzierungen \1, \2... den genauen Text der entsprechenden Gruppe, zB angepasst entsprechen \(fo*\)\(ba*\)\1Matches foobaafooaber nicht foobaafo. Es gibt keine Standardmethode, um auf die 10. Gruppe und darüber hinaus zu verweisen (die Standardbedeutung von \10ist die erste Gruppe, gefolgt von a 0).

Die folgenden Funktionen sind ebenfalls Standard, fehlen jedoch bei einigen eingeschränkten Implementierungen:

  • \{m,n\}stimmt mit dem vorhergehenden Zeichen oder Unterausdruck zwischen m und n überein ; n oder m können weggelassen werden und bedeuten genau m .\{m\}
  • In Klammern können Zeichenklassen verwendet werden, die beispielsweise [[:alpha:]]mit einem beliebigen Buchstaben übereinstimmen. Moderne Implementierungen von Klammerausdrücken umfassen auch Sortierelemente wie [.ll.]und Äquivalenzklassen wie [=a=].

Die folgenden Erweiterungen kommen häufig vor (insbesondere in GNU-Tools), sind jedoch nicht in allen Implementierungen enthalten. Überprüfen Sie das Handbuch des verwendeten Tools.

  • \|zur Abwechslung: foo\|barStreichhölzer foooder bar.
  • \?(kurz für \{0,1\}) und \+(kurz für \{1,\}) stimmen höchstens 1 Mal bzw. mindestens 1 Mal mit dem vorhergehenden Zeichen oder Unterausdruck überein.
  • \nEntspricht einem Zeilenumbruch, \teinem Tab usw.
  • \wStimmt mit jedem Wortbestandteil überein (kurz, [_[:alnum:]]aber mit Variation, wenn es um Lokalisierung geht) und \Wstimmt mit jedem Zeichen überein, das kein Wortbestandteil ist.
  • \<und \>die leere Zeichenkette nur am Anfang bzw. Ende eines Wortes suchen; \bpasst auch und \Bpasst wo \bnicht.

Beachten Sie, dass Werkzeuge ohne \|Operator nicht die volle Kraft regulärer Ausdrücke haben. Rückverweise ermöglichen einige zusätzliche Dinge, die mit regulären Ausdrücken im mathematischen Sinne nicht möglich sind.

Erweiterte reguläre Ausdrücke (ERE)

Erweiterte reguläre Ausdrücke werden durch den POSIX-Standard codiert . Ihr Hauptvorteil gegenüber BRE ist die Regelmäßigkeit: Alle Standardoperatoren sind bloße Interpunktionszeichen, ein Backslash, bevor ein Interpunktionszeichen ihn immer in Anführungszeichen setzt. Es ist die Syntax von awk, grep -Eoder egrep, GNU sed -r, und bash=~ - Operator. Diese Syntax bietet die folgenden Funktionen:

  • ^und $stimmen nur am Anfang und Ende einer Zeile überein.
  • . Entspricht einem beliebigen Zeichen (oder einem beliebigen Zeichen außer einem Zeilenumbruch).
  • […]Entspricht einem beliebigen Zeichen in Klammern (Zeichensatz). Die Ergänzung mit einem Anfangsbuchstaben ^und Bereichen funktioniert wie in BRE (siehe oben). Zeichenklassen können verwendet werden, fehlen aber in einigen Implementierungen. Moderne Implementierungen unterstützen auch Äquivalenzklassen und Sortierelemente. Ein Backslash in eckigen Klammern zitiert das nächste Zeichen in einigen, aber nicht allen Implementierungen. Damit ist \\ein Backslash für die Portabilität gemeint.
  • (…)ist eine syntaktische Gruppe zur Verwendung mit *oder als \DIGITErsatz.
  • |zur Abwechslung: foo|barStreichhölzer foooder bar.
  • *, +und ?stimmt mehrmals mit dem vorhergehenden Zeichen oder Unterausdruck überein: 0 oder mehr für *, 1 oder mehr für +, 0 oder 1 für ?.
  • Backslash zitiert das nächste Zeichen, wenn es nicht alphanumerisch ist.
  • {m,n}stimmt mit dem vorhergehenden Zeichen oder Unterausdruck zwischen m und n überein (fehlt in einigen Implementierungen); n oder m können weggelassen werden und bedeuten genau m .{m}
  • Einige gebräuchliche Erweiterungen wie in BRE: Rückverweise (insbesondere in awk nicht vorhanden, außer in der busybox-Implementierung, in der Sie sie verwenden können ); Sonderzeichen , etc .; Wortgrenzen und Wortbestandteile und …\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (Perl-kompatible reguläre Ausdrücke)

PCRE sind Erweiterungen von ERE, die ursprünglich von Perl eingeführt und von GNU grep -Pund vielen modernen Tools und Programmiersprachen übernommen wurden , normalerweise über die PCRE- Bibliothek. In der Perl-Dokumentation finden Sie eine schöne Formatierung mit Beispielen. Nicht alle Funktionen der neuesten Version von Perl werden von PCRE unterstützt (z. B. wird die Ausführung von Perl-Code nur in Perl unterstützt). Eine Zusammenfassung der unterstützten Funktionen finden Sie im PCRE-Handbuch . Die wichtigsten Ergänzungen zu ERE sind:

  • (?:…)ist eine nicht erfassende Gruppe: wie (…), zählt aber nicht für Rückverweise.
  • (?=FOO)BAR(Lookahead-) Übereinstimmungen BAR, aber nur, wenn es auch eine Übereinstimmung gibt, um FOOan derselben Position zu beginnen. Dies ist am nützlichsten, um eine Übereinstimmung zu verankern, ohne den folgenden Text in die Übereinstimmung einzubeziehen: foo(?=bar)Übereinstimmungen, foojedoch nur, wenn darauf gefolgt wird bar.
  • (?!FOO)BAR(negativer Lookahead) passt BAR, aber es gibt nicht auch eine Übereinstimmung für FOOdieselbe Position. Zum Beispiel (?!foo)[a-z]+stimmt mit jedem Wort in Kleinbuchstaben überein, das nicht mit "" beginnt foo. [a-z]+(?![0-9)Stimmt mit jedem Wort in Kleinbuchstaben überein, auf das keine Ziffer folgt ( foo123es stimmt also überein, foaber nicht foo).
  • (?<=FOO)BAR(Lookbehind-) Übereinstimmungen BAR, aber nur, wenn unmittelbar davor eine Übereinstimmung für steht FOO. FOOmuss eine bekannte Länge haben (Sie können keine Wiederholungsoperatoren wie verwenden *). Dies ist am nützlichsten, um eine Übereinstimmung zu verankern, ohne den vorhergehenden Text in die Übereinstimmung einzubeziehen: (?<=^| )fooÜbereinstimmungen, foojedoch nur, wenn ein Leerzeichen oder der Anfang der Zeichenfolge vorangestellt ist.
  • (?<!FOO)BAR(negative lookbehind) -Matches BAR, aber nur, wenn kein Match für unmittelbar davor steht FOO. FOOmuss eine bekannte Länge haben (Sie können keine Wiederholungsoperatoren wie verwenden *). Dies ist am nützlichsten, um eine Übereinstimmung zu verankern, ohne den vorhergehenden Text in die Übereinstimmung einzuschließen: (?<![a-z])fooÜbereinstimmungen, foojedoch nur, wenn kein Kleinbuchstabe vorangestellt ist.

Emacs

Die Syntax von Emacs liegt zwischen BRE und ERE. Zusätzlich zu Emacs ist dies die Standardsyntax für -regexGNU find. Emacs bietet die folgenden Operatoren an:

  • ^, $, ., […], *, +, ?Wie in ERE
  • \(…\), \|, \{…\}, Wie in BRE\DIGIT
  • mehr Backslash-Letter-Sequenzen ; \<und \>für Wortgrenzen; und mehr in neueren Versionen von Emacs, die in anderen Engines mit einer Emacs-ähnlichen Syntax häufig nicht unterstützt werden.

Shell Klackse

Shell-Globs (Platzhalter) führen einen Musterabgleich mit einer Syntax durch, die sich von regulären Ausdrücken völlig unterscheidet und weniger leistungsfähig ist. Neben Shells sind diese Platzhalter auch mit anderen Tools wie find -nameund Rsync-Filtern verfügbar . POSIX-Muster umfassen die folgenden Funktionen:

  • ? Stimmt mit jedem einzelnen Zeichen überein.
  • […]ist ein Zeichensatz wie in üblichen regulären Ausdruckssyntaxen. Einige Shells unterstützen keine Zeichenklassen. Einige Schalen benötigen !statt ^den Satz zu negieren.
  • *Stimmt mit einer beliebigen Zeichenfolge überein (häufig, außer /wenn Dateipfade übereinstimmen; wenn von /ausgeschlossen *, **schließt dies manchmal ein /, aber überprüfen Sie die Dokumentation des Tools).
  • Backslash zitiert das nächste Zeichen.

Ksh bietet zusätzliche Funktionen, die dem Pattern Matching die volle Kraft regulärer Ausdrücke verleihen. Diese Funktionen sind auch in bash nach dem Ausführen verfügbar shopt -s extglob. Zsh hat eine andere Syntax , kann aber auch die Syntax von ksh unterstützen setopt ksh_glob.

Gilles
quelle
Andere reiche REs Sie können erwähnen sind vim‚s und AT & T libast (wie in ksh93) one.
Stéphane Chazelas
@ StéphaneChazelas Abgesehen von vim verwendet welches Programm vim regexps? Welches Programm verwendet neben ksh libast?
Gilles
alle von der AT & T - Werkzeug - Set verwenden die AT & T REs ( grep, tw, expr...). Mit Ausnahme kshist , dass Toolset selten obwohl außerhalb von AT & T gefunden.
Stéphane Chazelas
Nach meinem Verständnis (und dem von Wikipedia) bezieht sich Ihr Begriff "Zeichenklasse" tatsächlich auf "POSIX-Zeichenklasse" ... regex(7)stimmt jedoch mit Ihnen überein und nennt [these]"Klammerausdrücke" und (innerhalb von "Klammerausdrücken") [:these:]"Zeichenklassen". Ich bin mir nicht sicher, wie ich das am besten angehen soll.
Adam Katz
Wie auch immer Sie sie nennen, sie unterstützen Reichweiten. Es ist definitiv erwähnenswert, dass dies -einen Bereich angibt und entweder zuerst (nach dem optionalen ^) oder zuletzt maskiert werden sollte , wenn dies wörtlich genommen werden soll. (Ich habe viele Fehler gesehen, die z. [A-z]B. auf die Änderung in Groß- / Kleinschreibung zurückzuführen sind. Diese stimmen mit den Zeichen der Codes 65 bis 122 überein und enthalten aus Versehen jeweils Folgendes:. [\]^_`Ich habe auch gesehen, dass die Gültigkeit [!-~]aller druckbaren Zeichen in ANSI noch verwirrend ist , die ich lieber zu sehen , wie [\x21-\x7e], die zumindest einfach in ihrer Wirkung jedoch verwirrend in einer anderen Dimension ist).
Adam Katz