5431 Stimmen

Regulärer Ausdruck, um eine Zeile zu finden, die kein Wort enthält

Ich weiß, dass es möglich ist, ein Wort abzugleichen und dann die Übereinstimmungen mit anderen Tools umzukehren (z. B. grep -v). Ist es jedoch möglich, Zeilen abzugleichen, die kein bestimmtes Wort enthalten, z. B. hede, unter Verwendung eines regulären Ausdrucks?

Eingabe:
hoho
hihi
haha
hede
Code:
grep "" input
Gewünschte Ausgabe:
hoho
hihi
haha

108 Stimmen

Wahrscheinlich ein paar Jahre zu spät, aber was ist falsch mit: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? Die Idee ist einfach. Fahren Sie mit dem Abgleich fort, bis Sie den Beginn des unerwünschten Strings sehen, und gleichen Sie dann nur in den N-1 Fällen ab, in denen der String nicht abgeschlossen ist (wobei N die Länge des Strings ist). Diese N-1 Fälle sind "h gefolgt von nicht-e", "he gefolgt von nicht-d" und "hed gefolgt von nicht-e". Wenn es Ihnen gelungen ist, diese N-1 Fälle zu bestehen, haben Sie den unerwünschten String erfolgreich nicht abgeglichen, sodass Sie erneut mit der Suche nach [^h]* beginnen können.

441 Stimmen

@stevendesu: Versuche dies für 'ein-sehr-sehr-langes-Wort' oder noch besser einen halben Satz. Viel Spaß beim Tippen. Übrigens, es ist fast unleserlich. Weiß nicht über den Leistungseinfluss.

14 Stimmen

@PeterSchuetze: Sicher ist es nicht schön für sehr sehr lange Wörter, aber es ist eine praktikable und korrekte Lösung. Obwohl ich keine Tests zur Leistung durchgeführt habe, würde ich mir vorstellen, dass es nicht allzu langsam ist, da die meisten nachfolgenden Regeln ignoriert werden, bis Sie ein h sehen (oder den ersten Buchstaben des Wortes, Satzes usw. sehen). Und Sie könnten den Regex-String für lange Zeichenfolgen leicht mithilfe iterativer Konkatenation generieren. Wenn es funktioniert und schnell generiert werden kann, ist Lesbarkeit wichtig? Dafür sind Kommentare da.

12voto

Avinash Raj Punkte 165992

Durch das PCRE-Verb (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Dies überspringt vollständig die Zeile, die den genauen String hede enthält, und passt zu allen verbleibenden Zeilen.

DEMO

Ausführung der Teile:

Betrachten wir das obige Regex, indem wir es in zwei Teile aufteilen.

  1. Teil vor dem |-Symbol. Der Teil sollte nicht übereinstimmen.

    ^hede$(*SKIP)(*F)
  2. Teil nach dem |-Symbol. Der Teil sollte übereinstimmen.

    ^.*$

TEIL 1

Der Regex-Interpreter wird mit der Ausführung vom ersten Teil beginnen.

^hede$(*SKIP)(*F)

Erklärung:

  • ^ Signalisiert den Anfang.
  • hede Passt zum String hede
  • $ Signalisiert das Zeilenende.

Die Zeile, die den String hede enthält, würde übereinstimmen. Sobald der Regex-Interpreter das (*SKIP)(*F) sieht (Hinweis: Du könntest (*F) als (*FAIL) schreiben) Verb, überspringt er und führt die Übereinstimmung zum Scheitern. Dem |-Symbol, das das Änderungs- oder logische ODER-Operator genannt wird, wird der PCRE-Verb hinzugefügt, der wiederum alle Grenzen zwischen jedem einzelnen Zeichen auf allen Zeilen außer der Zeile mit dem genauen String hede abgleicht. Siehe das Demo hier. Das heißt, es versucht, die Zeichen aus dem verbleibenden String abzugleichen. Jetzt wird der Regex im zweiten Teil ausgeführt.

TEIL 2

^.*$

Erklärung:

  • ^ Signalisiert den Anfang. Das heißt, es passt zu allen Zeilenanfängen außer der in der hede-Zeile. Siehe das Demo hier.

  • .* Im Multiline-Modus würde . jedes Zeichen außer Zeilenumbrüchen oder Wagenrücklaufzeichen abgleichen. Und * wiederholt das vorherige Zeichen null oder mehrmals. Also würde .* die ganze Zeile abgleichen. Siehe das Demo hier.

    Hey warum hast du .* hinzugefügt statt .+ ?

    Weil .* eine leere Zeile abgleicht, aber .+ keine leere abgleichen würde. Wir wollen alle Zeilen außer hede abgleichen, es könnte auch leere Zeilen im Eingang geben. Daher musst du .* anstelle von .+ verwenden. .+ würde das vorherige Zeichen einmal oder mehrmals wiederholen. Siehe, .* gleicht einer leeren Zeile ab hier.

  • $ Das Zeilenende-Anker ist hier nicht notwendig.

9voto

Kaz Punkte 51547

Die TXR-Sprache unterstützt die Negation von Regex.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Eingabe

Ein komplizierteres Beispiel: Übereinstimmung mit allen Zeilen, die mit a beginnen und mit z enden, aber die Teilzeichenfolge hede nicht enthalten:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echo
az
abcz       <- echo
abcz
abhederz   <- nicht echo; enthält hede
ahedez     <- nicht echo; enthält hede
ace        <- nicht echo; endet nicht mit z
ahedz      <- echo
ahedz

Die Negation von Regex ist allein nicht besonders nützlich, aber wenn auch noch die Schnittmenge vorhanden ist, wird es interessant, da Sie eine vollständige Menge von booleschen Mengenoperationen haben: Sie können "die Menge, die dies erfüllt, außer Dingen, die das erfüllen" ausdrücken.

0 Stimmen

Bitte beachten Sie, dass dies auch die Lösung für das auf Lucene basierende Regex von ElasticSearch ist.

8voto

andrew pate Punkte 3251

Es ist möglicherweise wartungsfreundlicher, zwei reguläre Ausdrücke in Ihrem Code zu verwenden, einen für das erste Match und einen für das zweite Match, um Ausreißerfälle zu blockieren, die Sie beispielsweise mit ^.*(hede).* prüfen möchten. Dann haben Sie die geeignete Logik in Ihrem Code.

OK, ich gebe zu, dass dies keine wirklich Antwort auf die gestellte Frage ist und möglicherweise auch etwas mehr Verarbeitung erfordert als nur ein einzelner regulärer Ausdruck. Aber für Entwickler, die hier nach einer schnellen Notfalllösung für einen Ausreißerfall suchen, sollte diese Lösung nicht außer Acht gelassen werden.

6voto

Daniel Nyamasyo Punkte 2238

Die nachstehende Funktion hilft Ihnen, die gewünschte Ausgabe zu erhalten

 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }

?>

6voto

Matthew Rideout Punkte 5517

Ich wollte ein weiteres Beispiel hinzufügen, wenn Sie versuchen, eine gesamte Zeile abzugleichen, die den String X enthält, aber nicht auch den String Y enthält.

Zum Beispiel, nehmen wir an, wir möchten überprüfen, ob unsere URL / String "tasty-treats" enthält, solange er nicht auch irgendwo "Schokolade" enthält.

Dieses Regex-Muster würde funktionieren (funktioniert auch in JavaScript)

^(?=.*?tasty-treats)((?!chocolate).)*$

(globale, multiline Flags im Beispiel)

Interaktives Beispiel: https://regexr.com/53gv4

Übereinstimmungen

(Diese URLs enthalten "tasty-treats" und enthalten auch nicht "Schokolade")

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

Keine Übereinstimmung

(Diese URLs enthalten irgendwo "Schokolade" - sie werden also nicht übereinstimmen, auch wenn sie "tasty-treats" enthalten)

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X