5307 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

105 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.

427 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.

7328voto

Bart Kiers Punkte 160101

Die Vorstellung, dass Regex das inverse Matchen nicht unterstützt, ist nicht ganz richtig. Sie können dieses Verhalten nachahmen, indem Sie negative Look-arounds verwenden:

^((?!hede).)*$

Das obige Regex wird auf jede Zeichenkette oder Zeile ohne Zeilenumbruch passen, die das (Teil-)Zeichen 'hede' nicht enthält. Wie bereits erwähnt, ist dies nicht etwas, was Regex "gut" kann (oder tun sollte), aber es ist dennoch möglich.

Und wenn Sie auch Zeilenumbruchzeichen passen müssen, verwenden Sie den DOT-ALL Modifier (das abschließende s im folgenden Muster):

/^((?!hede).)*$/s

oder verwenden Sie es inline:

/(?s)^((?!hede).)*$/

(wobei die /.../ die Regex-Trennzeichen sind, also nicht Teil des Musters)

Wenn der DOT-ALL Modifier nicht verfügbar ist, können Sie das gleiche Verhalten mit der Zeichenklasse [\s\S] nachahmen:

/^((?!hede)[\s\S])*$/

Erklärung

Eine Zeichenkette ist einfach eine Liste von n Zeichen. Vor und nach jedem Zeichen gibt es eine leere Zeichenkette. Daher wird eine Liste von n Zeichen n+1 leere Zeichenketten haben. Betrachten Sie die Zeichenkette "ABhedeCD":

S = e1 A e2 B e3 h e4 e e5 d e6 e e7 C e8 D e9

index    0      1      2      3      4      5      6      7

wo die e's die leeren Zeichenketten sind. Das Regex (?!hede). schaut voraus, um zu sehen, ob die Teilzeichenkette "hede" nicht zu sehen ist, und wenn das der Fall ist (also etwas anderes zu sehen ist), dann wird der Punkt . auf jedes Zeichen außer einem Zeilenumbruch passen. Look-arounds werden auch als Zero-Width-Assertions bezeichnet, weil sie keine Zeichen verbrauchen. Sie bestätigen/validieren nur etwas.

Also werden in meinem Beispiel zunächst alle leeren Zeichenketten validiert, um zu sehen, ob vor dem Verzehren eines Zeichens kein "hede" voraus ist, bevor der Punkt . (dot) auf ein Zeichen passt. Das Regex (?!hede). wird das nur einmal tun, daher ist es in einer Gruppe eingeschlossen und wird null oder mehrmals wiederholt: ((?!hede).)*. Schließlich sind der Anfang und das Ende der Eingabe verankert, um sicherzustellen, dass die gesamte Eingabe verbraucht wird: ^((?!hede).)*$

Wie Sie sehen können, wird die Eingabe "ABhedeCD" scheitern, weil beim e3 das Regex (?!hede) scheitert (da tatsächlich "hede" voraus ist!).

43 Stimmen

Ich würde nicht so weit gehen zu sagen, dass dies etwas ist, wombei Regex schlecht ist. Die Bequemlichkeit dieser Lösung ist ziemlich offensichtlich und der Leistungsverlust im Vergleich zu einer programmatischen Suche wird oft unbedeutend sein.

54 Stimmen

Im engeren Sinne macht negativer Ausblick Ihr regulären Ausdruck nicht-regulär.

97 Stimmen

@PeterK, sicher, aber dies ist SO und nicht MathOverflow oder CS-Stackexchange. Menschen, die hier eine Frage stellen, suchen normalerweise nach einer praktischen Antwort. Die meisten Bibliotheken oder Tools (wie grep, das der OP erwähnt) mit Regex-Unterstützung haben alle Funktionen, die sie theoretisch nicht-regulär machen.

953voto

JoshuaDavid Punkte 7963

Beachten Sie, dass die Lösung nicht mit "hede" beginnt:

^(?!hede).*$

im Allgemeinen viel effizienter ist als die Lösung enthält nicht "hede" :

^((?!hede).)*$

Die erstere überprüft nur die Position "hede" an der Eingabeschnur an erster Stelle, anstatt bei jeder Position.

7 Stimmen

Danke, ich habe es verwendet, um zu überprüfen, dass der String keine Zahlenfolge von ^((?!\d{5,}).)* enthält.

0 Stimmen

^((?!hede).)*$ hat für mich funktioniert, indem ich das jQuery DataTable-Plugin verwendet habe, um einen String aus dem Datensatz auszuschließen

5 Stimmen

Hallo! Ich kann keinen endet nicht mit "hede" Regex erstellen. Können Sie mir dabei helfen?

246voto

Athena Punkte 3010

Wenn Sie es nur für grep verwenden, können Sie grep -v hede verwenden, um alle Zeilen zu erhalten, die nicht hede enthalten.

ETA Oh, beim erneuten Lesen der Frage ist grep -v wahrscheinlich das, was Sie mit "Tools-Optionen" gemeint haben.

31 Stimmen

Tipp: Um schrittweise auszufiltern, was Sie nicht möchten: grep -v "hede" | grep -v "hihi" | ...etc.

61 Stimmen

Oder verwenden Sie nur einen Prozess grep -v -e hede -e hihi -e ...

23 Stimmen

Oder einfach grep -v "hede\|hihi" :)

241voto

Jessica Punkte 23

Antwort:

^((?!hede).)*$

Erklärung:

^zeigt den Anfang des Strings an, ( Gruppe und speichert in \1 (0 oder mehrmals (so oft wie möglich)),
(?! schaut voraus, ob nicht,

hede in deinem String ist,

) Ende des Vorausblicks, . jedes Zeichen außer \n,
)* Ende von \1 (Hinweis: Da du einen Quantifizierer auf diese Erfassung anwendest, wird nur die LETZTE Wiederholung des erfassten Musters in \1 gespeichert)
$ vor einem optionalen \n und dem Ende des Strings

21 Stimmen

Toll, das hat bei mir in Sublime Text 2 funktioniert, indem ich mehrere Wörter verwendet habe '^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'

4 Stimmen

@DamodarBashyal Ich weiß, ich bin ziemlich spät dran, aber du könntest das zweite Element dort einfach entfernen und du würdest die exakt gleichen Ergebnisse erhalten.

0 Stimmen

@forresthopkinsa Was meinen Sie mit dem zweiten Begriff in Ihrem Kommentar?

116voto

Hades32 Punkte 894

Die gegebenen Antworten sind völlig in Ordnung, nur ein akademischer Punkt:

Reguläre Ausdrücke im Sinne der theoretischen Informatik KÖNNEN ES NICHT so machen. Für sie müsste es so aussehen:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Dies führt nur zu einem VOLLSTÄNDIGEN Treffer. Es wäre noch umständlicher, dies für Teilübereinstimmungen zu tun.

1 Stimmen

Wichtig zu beachten ist, dass dies nur grundlegende POSIX.2 reguläre Ausdrücke verwendet und daher, obwohl knapp, portabler ist, wenn PCRE nicht verfügbar ist.

7 Stimmen

Ich stimme zu. Viele, wenn nicht die meisten regulären Ausdrücke sind keine regulären Sprachen und könnten nicht von einem endlichen Automaten erkannt werden.

0 Stimmen

@ThomasMcLeod, Hades32: Ist es im Rahmen einer beliebigen regulären Sprache möglich, ' not ' und ' and ' sowie das ' or ' eines Ausdrucks wie ' (hede|Hihi) ' zu sagen? (Dies könnte eine Frage für CS sein.)

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