--Edit-- Die aktuellen Antworten haben einige nützliche Ideen, aber ich möchte etwas vollständigeres, das ich zu 100% verstehen und wiederverwenden kann. Deshalb habe ich ein Kopfgeld gesetzt. Auch Ideen, die überall funktionieren, sind für mich besser als keine Standard-Syntax wie\K
In dieser Frage geht es darum, wie ich ein Muster mit Ausnahme einiger Situationen s1 s2 s3 abgleichen kann. Ich gebe ein spezifisches Beispiel, um meine Bedeutung zu zeigen, bevorzuge aber eine allgemeine Antwort, die ich zu 100% verstehen kann, damit ich sie in anderen Situationen wiederverwenden kann.
Beispiel
Ich möchte fünf Ziffern mit \b\d{5}\b
s1 s2 s3 abgleichen , aber nicht in drei Situationen:
s1: Nicht in einer Zeile, die mit einem Punkt wie diesem Satz endet.
s2: Nicht irgendwo in Parens.
s3: Nicht in einem Block, der mit beginnt if(
und mit endet//endif
Ich weiß, wie man eines von s1 s2 s3 mit einem Lookahead und Lookbehind löst, besonders in C # Lookbehind oder \K
in PHP.
Zum Beispiel
s1 (?m)(?!\d+.*?\.$)\d+
s3 mit C # Lookbehind (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
s3 mit PHP \ K. (?:(?:if\(.*?//endif)\D*)*\K\d+
Aber die Mischung der Bedingungen zusammen lässt meinen Kopf explodieren. Noch schlimmer ist, dass ich möglicherweise zu einem anderen Zeitpunkt andere Bedingungen s4 s5 hinzufügen muss.
Die gute Nachricht ist, dass es mir egal ist, ob ich die Dateien mit den gängigsten Sprachen wie PHP, C #, Python oder der Waschmaschine meines Nachbarn verarbeite. :) Ich bin so ziemlich ein Anfänger in Python & Java, aber interessiert zu erfahren, ob es eine Lösung gibt.
Also bin ich hergekommen, um zu sehen, ob jemand an ein flexibles Rezept denkt.
Hinweise sind in Ordnung: Sie müssen mir keinen vollständigen Code geben. :) :)
Danke dir.
\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."if("
open paren geschlossen wird, nicht mit a")"
, sondern mit a :"//endif"
? Und wenn Sie für s3 wirklich gemeint haben, dass die if-Klausel mit: geschlossen werden soll"//endif)"
, dann ist die s3-Anforderung eine Teilmenge von s2.especially in C# lookbehind or \K in PHP
... Aber C # sieht nicht nur C # aus, es ist .NET, also kannst du dich auch beschweren, ich sage C # nicht .NET :) Und als Antwort sage ich Ruby nicht Onigurama, das ist auch schlecht ... Gibt es eine andere Sprache, die PCRE verwendet? Wenn ich nicht über Notepad ++ oder Server-Tools spreche, ist dies eine Frage zur Verwendung der Funktion in der Sprache. Ich hoffe, das erklärt und entschuldigt, wenn es falsch aussiehtAntworten:
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).
Hauptfakt
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
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:
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:
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.
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:
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:
In deinem Fall:
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 nur
whatYouWant
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.
<a stuff...>...</a>
?<i>
Tag oder einem Javascript-Snippet abgleichen (weitere Bedingungen)?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.
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 RezeptWhatYouWant
(Erfassung in Gruppe 1)s1|s2|s3|(WhatYouWant)
richtig war? Um alle Instanzen von zu löschenWhatYouWant
, ändern Sie den regulären Ausdruck inFür die Ersatzzeichenfolge verwenden Sie
$1
. Was hier passiert, ist, dass für jede Instanzs1|s2|s3
, die übereinstimmt, der Ersatz$1
diese Instanz durch sich selbst ersetzt (referenziert von$1
). WennWhatYouWant
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.
(*SKIP)(*F)
oben genannte Variante, um genau das zu erreichen, was Sie möchten, und führen Sie einen direkten Austausch durch.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.
quelle
Tarzan
, aber nicht, wenn Sie sich in doppelten Anführungszeichen befinden. Der:/no|no|(yes)/
Trick-Regex wäre so etwas wie:/"[^"]*"|Tarzan/
(Ignorieren von entkommenen Zeichen). Dies funktioniert in vielen Fällen, schlägt jedoch vollständig fehl, wenn es auf den folgenden gültigen JavaScript-Text angewendet wird :var bug1 = 'One " quote here. Should match this Tarzan'; var bug2 = "Should not match this Tarzan";
. Der Trick von Rex funktioniert nur, wenn ALLE möglichen Strukturen übereinstimmen - mit anderen Worten - Sie müssen den Text vollständig analysieren, um eine 100% ige Genauigkeit zu gewährleisten.var bug1 = /"[^"]*"|(Tarzan)/gi;
und hatte den gleichen Effekt (und dieses zweite Beispiel ist sicherlich kein Randfall). Es gibt noch viele weitere Beispiele, bei denen diese Technik nicht zuverlässig funktioniert.(?<!\\)"(?:\\"|[^"\r\n])*+"
Sie ziehen die großen Waffen nicht, es sei denn, Sie haben einen Grund." Das Prinzip der Lösung gilt weiterhin. Wenn wir nicht in der Lage sind, ein Muster für die linke Seite auszudrücken, ist das eine andere Geschichte, wir brauchen eine andere Lösung. Aber die Lösung macht das, was sie bewirbt.Führen Sie drei verschiedene Übereinstimmungen durch und behandeln Sie die Kombination der drei Situationen mithilfe der programminternen bedingten Logik. Sie müssen nicht alles in einem riesigen regulären Ausdruck erledigen.
EDIT: lass mich etwas erweitern, weil die Frage gerade interessanter geworden ist :-)
Die allgemeine Idee, die Sie hier erfassen möchten, besteht darin, mit einem bestimmten Regex-Muster übereinzustimmen, jedoch nicht, wenn bestimmte andere (möglicherweise beliebig viele) Muster in der Testzeichenfolge vorhanden sind. Glücklicherweise können Sie Ihre Programmiersprache nutzen: Halten Sie die regulären Ausdrücke einfach und verwenden Sie einfach eine zusammengesetzte Bedingung. Eine bewährte Methode wäre, diese Idee in einer wiederverwendbaren Komponente zu erfassen. Erstellen wir also eine Klasse und eine Methode, die sie implementiert:
Also haben wir oben die Suchzeichenfolge (die fünf Ziffern) und mehrere Ausnahmezeichenfolgen (Ihre s1 , s2 und s3 ) eingerichtet und dann versucht, sie mit mehreren Testzeichenfolgen abzugleichen. Die gedruckten Ergebnisse sollten den Kommentaren in den Kommentaren neben jeder Testzeichenfolge entsprechen.
quelle
Ihre Forderung, dass es nicht innerhalb von Parens ist, ist unmöglich, in allen Fällen zu befriedigen. Wenn Sie nämlich irgendwie
(
links und)
rechts eine finden können, bedeutet dies nicht immer, dass Sie sich in Parens befinden. Z.B.(....) + 55555 + (.....)
- noch nicht in parens gibt es(
und)
links und rechtsJetzt könnten Sie sich für schlau halten und
(
nur dann nach links suchen , wenn Sie)
vorher nicht begegnen, und umgekehrt nach rechts. Dies funktioniert in diesem Fall nicht:((.....) + 55555 + (.....))
- innerhalb der Eltern, obwohl es schließen)
und(
links und rechts.Es ist unmöglich herauszufinden, ob Sie sich mit Regex in Parens befinden, da Regex nicht zählen kann, wie viele Parens geöffnet und wie viele geschlossen wurden.
Betrachten Sie diese einfachere Aufgabe: Stellen Sie mithilfe von Regex fest, ob alle (möglicherweise verschachtelten) Parens in einer Zeichenfolge geschlossen sind, dh für alle, die
(
Sie suchen müssen)
. Sie werden feststellen, dass es unmöglich ist, es zu lösen, und wenn Sie das nicht mit Regex lösen können, können Sie nicht herausfinden, ob sich ein Wort in allen Fällen innerhalb der Parens befindet, da Sie an einer bestimmten Position in der Zeichenfolge nicht herausfinden können, ob alle vorhergehenden(
haben eine entsprechende)
.quelle
Hans, wenn es dir nichts ausmacht, habe ich die Waschmaschine deines Nachbarn namens Perl benutzt :)
Bearbeitet: Unterhalb eines Pseudocodes:
Angesichts der Datei input.txt:
Und das Skript validator.pl:
Ausführung:
quelle
Ich bin mir nicht sicher, ob dies Ihnen helfen würde oder nicht, aber ich biete eine Lösung unter Berücksichtigung der folgenden Annahmen:
Allerdings habe ich auch folgendes berücksichtigt -
if(
Blöcke im Auge zu behalten .Ok hier ist die Lösung -
Ich habe C # und damit MEF (Microsoft Extensibility Framework) verwendet, um die konfigurierbaren Parser zu implementieren. Die Idee ist, einen einzelnen Parser zum Parsen und eine Liste konfigurierbarer Validierungsklassen zu verwenden, um die Zeile zu validieren und basierend auf der Validierung true oder false zurückzugeben. Dann können Sie jederzeit einen beliebigen Validator hinzufügen oder entfernen oder neue hinzufügen, wenn Sie möchten. Bisher habe ich bereits für S1, S2 und S3 implementiert, die Sie erwähnt haben. Überprüfen Sie die Klassen unter Punkt 3. Sie müssen Klassen für s4, s5 hinzufügen, wenn Sie dies in Zukunft benötigen.
Erstellen Sie zunächst die Schnittstellen -
Dann kommt der Dateireader und Checker -
Dann kommt die Implementierung einzelner Prüfer, die Klassennamen sind selbsterklärend, daher denke ich nicht, dass sie weitere Beschreibungen benötigen.
Das Programm -
Zum Testen habe ich die Beispieldatei von @ Tiago genommen,
Test.txt
die die folgenden Zeilen enthielt:Gibt die Ausgabe -
Ich weiß nicht, ob dir das helfen würde oder nicht, ich hatte eine lustige Zeit damit zu spielen .... :)
Das Beste daran ist, dass zum Hinzufügen einer neuen Bedingung lediglich eine Implementierung bereitgestellt werden muss
IPatternMatcher
, diese automatisch aufgerufen und somit validiert wird.quelle
Wie @ zx81,
(*SKIP)(*F)
jedoch mit einer negativen Lookahead-Behauptung.DEMO
In Python würde mir das leicht gefallen,
Ausgabe:
quelle