Hans, ich nehme den Köder und konkretisiere meine frühere Antwort. Sie sagten, Sie wollen "etwas vollständigeres", also hoffe ich, dass Ihnen die lange Antwort nichts ausmacht - versuchen Sie nur zu gefallen. Beginnen wir mit Hintergrundinformationen.
Zunächst einmal ist dies eine ausgezeichnete Frage. Es gibt häufig Fragen zum Abgleichen bestimmter Muster, außer in bestimmten Kontexten (z. B. innerhalb eines Codeblocks oder in Klammern). Diese Fragen führen oft zu ziemlich umständlichen Lösungen. Ihre Frage zu mehreren Kontexten ist daher eine besondere Herausforderung.
Überraschung
Überraschenderweise gibt es mindestens eine effiziente Lösung, die allgemein, einfach zu implementieren und angenehm zu warten ist. Es funktioniert mit allen Regex-Varianten , mit denen Sie Erfassungsgruppen in Ihrem Code überprüfen können. Und es beantwortet eine Reihe häufig gestellter Fragen, die auf den ersten Blick anders klingen als Ihre: "Alles außer Donuts abgleichen", "Alle außer ... abgleichen", "Alle Wörter außer denen auf der schwarzen Liste meiner Mutter abgleichen", "Ignorieren" Tags "," Temperatur anpassen, sofern nicht kursiv geschrieben "...
Leider ist die Technik nicht gut bekannt: Ich schätze, dass in zwanzig SO-Fragen, die sie verwenden könnten, nur eine eine Antwort hat, die sie erwähnt - was vielleicht eine von fünfzig oder sechzig Antworten bedeutet. Siehe meinen Austausch mit Kobi in den Kommentaren. Die Technik wird ausführlich beschrieben diesem Artikel beschrieben, der sie (optimistisch) als den "besten Regex-Trick aller Zeiten" bezeichnet. Ohne auf so viele Details einzugehen, werde ich versuchen, Ihnen einen genauen Überblick über die Funktionsweise der Technik zu geben. Für weitere Details und Codebeispiele in verschiedenen Sprachen empfehle ich Ihnen, diese Ressource zu konsultieren.
Eine bekanntere Variante
Es gibt eine Variation unter Verwendung der für Perl und PHP spezifischen Syntax, die dasselbe erreicht. Sie werden es auf SO in den Händen von Regex-Meistern wie CasimiretHippolyte und HamZa sehen . Ich werde Ihnen weiter unten mehr darüber erzählen, aber mein Fokus liegt hier auf der allgemeinen Lösung, die mit allen Regex-Varianten funktioniert (solange Sie die Erfassungsgruppen in Ihrem Code überprüfen können).
Danke für den ganzen Hintergrund, zx81 ... Aber wie lautet das Rezept?
Hauptfakt
Die Methode gibt die Übereinstimmung in der Erfassung der Gruppe 1 zurück. Es kümmert sich überhaupt nicht um das Gesamtspiel.
Eigentlich, der Trick darin, die verschiedenen Kontexte, die wir nicht wollen, abzugleichen (diese Kontexte mit dem |
ODER / Wechsel zu verketten ) , um sie "zu neutralisieren". All unerwünschten Kontexte, der letzte Teil des Wechsels nach passender übereinstimmt , was wir tun wollen und fängt es an Gruppe 1.
Das allgemeine Rezept ist
Not_this_context|Not_this_either|StayAway|(WhatYouWant)
Dies wird übereinstimmen Not_this_context
, aber in gewissem Sinne wird diese Übereinstimmung in einen Papierkorb verschoben, da wir uns nicht die Gesamtübereinstimmungen ansehen: Wir betrachten nur die Erfassungen der Gruppe 1.
In Ihrem Fall können wir mit Ihren Ziffern und Ihren drei zu ignorierenden Kontexten Folgendes tun:
s1|s2|s3|(\b\d+\b)
Beachten Sie, dass die einzelnen Ausdrücke für s1, s2 und s3 als Tag klar bleiben können, da wir tatsächlich s1, s2 und s3 abgleichen, anstatt zu versuchen, sie mit Lookarounds zu vermeiden. (Sie sind die Unterausdrücke auf jeder Seite von a |
)
Der gesamte Ausdruck kann folgendermaßen geschrieben werden:
(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)
Siehe diese Demo (konzentrieren Sie sich jedoch auf die Erfassungsgruppen im unteren rechten Bereich.)
Wenn Sie mental versuchen, diesen regulären Ausdruck bei jedem zu teilen |
Ausdruck Trennzeichen , handelt es sich tatsächlich nur um eine Reihe von vier sehr einfachen Ausdrücken.
Für Aromen, die Freiräume unterstützen, liest sich dies besonders gut.
(?mx)
### s1: Match line that ends with a period ###
^.*\.$
| ### OR s2: Match anything between parentheses ###
\([^\)]*\)
| ### OR s3: Match any if(...//endif block ###
if\(.*?//endif
| ### OR capture digits to Group 1 ###
(\b\d+\b)
Dies ist außerordentlich einfach zu lesen und zu warten.
Den regulären Ausdruck erweitern
Wenn Sie mehr Situationen s4 und s5 ignorieren möchten, fügen Sie sie links abwechselnd hinzu:
s4|s5|s1|s2|s3|(\b\d+\b)
Wie funktioniert das?
Die Kontexte, die Sie nicht möchten, werden zu einer Liste von Änderungen auf der linken Seite hinzugefügt: Sie stimmen überein, aber diese Gesamtübereinstimmungen werden nie untersucht. Wenn Sie sie also abgleichen, können Sie sie in einen "Mülleimer" werfen.
Der gewünschte Inhalt wird jedoch in Gruppe 1 erfasst. Anschließend müssen Sie programmgesteuert überprüfen, ob Gruppe 1 festgelegt und nicht leer ist. Dies ist eine triviale Programmieraufgabe (und wir werden später darüber sprechen, wie es gemacht wird), insbesondere wenn man bedenkt, dass Sie einen einfachen regulären Ausdruck erhalten, den Sie auf einen Blick verstehen und nach Bedarf überarbeiten oder erweitern können.
Ich bin nicht immer ein Fan von Visualisierungen, aber diese zeigt sehr gut, wie einfach die Methode ist. Jede "Zeile" entspricht einer möglichen Übereinstimmung, aber nur die unterste Zeile wird in Gruppe 1 erfasst.
Debuggex-Demo
Perl / PCRE-Variation
Im Gegensatz zur obigen allgemeinen Lösung gibt es eine Variation für Perl und PCRE, die häufig bei SO zu sehen ist, zumindest in den Händen von Regex-Göttern wie @CasimiretHippolyte und @HamZa. Es ist:
(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant
In deinem Fall:
(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b
Diese Variante ist etwas einfacher zu verwenden, da der in den Kontexten s1, s2 und s3 übereinstimmende Inhalt einfach übersprungen wird, sodass Sie die Erfassungen der Gruppe 1 nicht überprüfen müssen (beachten Sie, dass die Klammern weg sind). Die Übereinstimmungen enthalten nurwhatYouWant
Beachten Sie, dass (*F)
, (*FAIL)
und (?!)
sind alle das Gleiche. Wenn Sie dunkler sein wollten, könnten Sie verwenden(*SKIP)(?!)
Demo für diese Version
Anwendungen
Hier sind einige häufige Probleme, die diese Technik oft leicht lösen kann. Sie werden feststellen, dass die Wortwahl dazu führen kann, dass einige dieser Probleme anders klingen, obwohl sie praktisch identisch sind.
- Wie kann ich foo außer irgendwo in einem Tag wie übereinstimmen
<a stuff...>...</a>
?
- Wie kann ich foo außer in einem
<i>
Tag oder einem Javascript-Snippet abgleichen (weitere Bedingungen)?
- Wie kann ich alle Wörter abgleichen, die nicht auf dieser schwarzen Liste stehen?
- Wie kann ich etwas in einem SUB ... END SUB-Block ignorieren?
- Wie kann ich alles außer ... s1 s2 s3 abgleichen?
So programmieren Sie die Captures der Gruppe 1
Sie haben den Code nicht gewählt, aber zur Vervollständigung ... Der Code zur Überprüfung der Gruppe 1 hängt natürlich von der Sprache Ihrer Wahl ab. In jedem Fall sollte der Code, mit dem Sie Übereinstimmungen überprüfen, nicht mehr als ein paar Zeilen enthalten.
Im Zweifelsfall empfehle ich Ihnen, den Abschnitt mit den Codebeispielen des zuvor erwähnten Artikels zu lesen, in dem Code für einige Sprachen dargestellt wird.
Alternativen
Abhängig von der Komplexität der Frage und der verwendeten Regex-Engine gibt es mehrere Alternativen. Hier sind die beiden, die für die meisten Situationen gelten können, einschließlich mehrerer Bedingungen. Meiner Meinung nach ist keines von beiden annähernd so attraktiv wie das s1|s2|s3|(whatYouWant)
Rezept, schon allein deshalb, weil immer die Klarheit gewinnt.
1. Ersetzen Sie dann Match.
Eine gute Lösung, die hackig klingt, aber in vielen Umgebungen gut funktioniert, besteht darin, in zwei Schritten zu arbeiten. Ein erster regulärer Ausdruck neutralisiert den Kontext, den Sie ignorieren möchten, indem er möglicherweise widersprüchliche Zeichenfolgen ersetzt. Wenn Sie nur übereinstimmen möchten, können Sie diese durch eine leere Zeichenfolge ersetzen und die Übereinstimmung im zweiten Schritt ausführen. Wenn Sie ersetzen möchten, können Sie zuerst die zu ignorierenden Zeichenfolgen durch etwas Besonderes ersetzen, z. B. um Ihre Ziffern herum mit einer Kette mit fester Breite von @@@
. Nach diesem Austausch können Sie das ersetzen, was Sie wirklich wollten. Dann müssen Sie Ihre markanten @@@
Zeichenfolgen zurücksetzen.
2. Lookarounds.
Ihr ursprünglicher Beitrag hat gezeigt, dass Sie verstehen, wie Sie eine einzelne Bedingung mithilfe von Lookarounds ausschließen können. Sie sagten, dass C # dafür großartig ist, und Sie haben Recht, aber es ist nicht die einzige Option. Die .NET-Regex-Varianten, die beispielsweise in C #, VB.NET und Visual C ++ zu finden sind, sowie das noch regex
zu ersetzende experimentelle Modulre
in Python ersetzt werden soll, sind die einzigen zwei mir bekannten Engines, die Lookbehind mit unendlicher Breite unterstützen. Mit diesen Tools kann eine Bedingung in einem Lookbehind dafür sorgen, dass nicht nur hinter, sondern auch auf das Match und darüber hinaus geschaut wird, sodass keine Koordination mit einem Lookahead erforderlich ist. Weitere Bedingungen? Weitere Lookarounds.
Wenn Sie den regulären Ausdruck, den Sie für s3 in C # hatten, recyceln, würde das gesamte Muster so aussehen.
(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
Aber jetzt weißt du, dass ich das nicht empfehle, oder?
Löschungen
@HamZa und @Jerry haben vorgeschlagen, einen zusätzlichen Trick für Fälle zu erwähnen, in denen Sie nur löschen möchten WhatYouWant
. Sie erinnern sich, dass das passende Rezept WhatYouWant
(Erfassung in Gruppe 1) s1|s2|s3|(WhatYouWant)
richtig war? Um alle Instanzen von zu löschen WhatYouWant
, ändern Sie den regulären Ausdruck in
(s1|s2|s3)|WhatYouWant
Für die Ersatzzeichenfolge verwenden Sie $1
. Was hier passiert, ist, dass für jede Instanz s1|s2|s3
, die übereinstimmt, der Ersatz $1
diese Instanz durch sich selbst ersetzt (referenziert von $1
). Wenn WhatYouWant
es dagegen übereinstimmt, wird es durch eine leere Gruppe und nichts anderes ersetzt - und daher gelöscht. Sehen Sie sich diese Demo an , danke @HamZa und @Jerry, dass Sie diese wunderbare Ergänzung vorgeschlagen haben.
Ersatz
Dies bringt uns zu Ersatz, auf die ich kurz eingehen werde.
- Wenn Sie durch nichts ersetzen, lesen Sie den obigen Trick "Löschen".
- Wenn Sie Perl oder PCRE verwenden, verwenden Sie beim Ersetzen die
(*SKIP)(*F)
oben genannte Variante, um genau das zu erreichen, was Sie möchten, und führen Sie einen direkten Austausch durch.
- Überprüfen Sie in anderen Varianten innerhalb des Ersetzungsfunktionsaufrufs die Übereinstimmung mit einem Rückruf oder Lambda und ersetzen Sie sie, wenn Gruppe 1 festgelegt ist. Wenn Sie dabei Hilfe benötigen, erhalten Sie in dem Artikel, auf den bereits verwiesen wird, Code in verschiedenen Sprachen.
Habe Spaß!
Nein, warte, da ist noch mehr!
Ah, nein, ich werde das für meine Memoiren in zwanzig Bänden speichern, die im nächsten Frühjahr veröffentlicht werden.
\K
ist keine spezielle PHP-Syntax. Bitte erläutern und klären Sie, was Sie sagen möchten. Wenn Sie uns mitteilen möchten, dass Sie keine "komplizierte" Lösung benötigen, müssen Sie sagen, was für Sie kompliziert ist und warum.