web-dev-qa-db-de.com

Regulärer Ausdruck für eine Zeile, die kein Wort enthält?

Ich weiß, dass es möglich ist, ein Word zu finden und die Übereinstimmungen mit anderen Tools (z. B. grep -v) umzukehren. Ich möchte jedoch gerne wissen, ob es möglich ist, Zeilen, die nicht ein bestimmtes Wort enthalten (z. B. hede), mithilfe eines regulären Ausdrucks zu finden. 

Eingang:

hoho
hihi
haha
hede

Code:

grep "<Regex for 'doesn't contain hede'>" input

Gewünschte Leistung: 

hoho
hihi
haha
3806
knaser

Der Gedanke, dass Regex kein inverses Matching unterstützt, ist nicht ganz richtig. Sie können dieses Verhalten nachahmen, indem Sie negative Look-arounds verwenden:

^((?!hede).)*$

Der obige reguläre Ausdruck passt zu jeder Zeichenfolge oder Zeile ohne Zeilenumbruch , die nicht die (Unter-) Zeichenfolge 'hede' enthält. Wie bereits erwähnt, ist dies nicht etwas, was Regex "gut" kann (oder sollte), aber dennoch ist möglich.

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

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

oder benutze es inline:

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

(wobei /.../ die Regex-Begrenzer sind, d. h. nicht Teil des Musters)

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

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

Erläuterung

Eine Zeichenfolge ist nur eine Liste von n Zeichen. Vor und nach jedem Zeichen befindet sich eine leere Zeichenfolge. Daher enthält eine Liste von n Zeichen n+1 leere Zeichenfolgen. Betrachten Sie die Zeichenfolge "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

dabei sind die e die leeren Zeichenfolgen. Der reguläre Ausdruck (?!hede). prüft, ob kein Teilstring "hede" zu sehen ist. Wenn dies der Fall ist (also etwas anderes zu sehen ist), stimmt der Punkt . überein Jedes Zeichen außer einem Zeilenumbruch. Look-arounds werden auch als Zero-Width-Assertions bezeichnet, da sie keine Zeichen verbrauchen . Sie behaupten/validieren nur etwas.

In meinem Beispiel wird jede leere Zeichenfolge zuerst überprüft, um festzustellen, ob kein "hede" im Voraus vorhanden ist, bevor ein Zeichen vom . (Punkt) verbraucht wird. Der reguläre Ausdruck (?!hede). macht das nur einmal, daher wird er in eine Gruppe eingeschlossen und null oder mehrmals wiederholt: ((?!hede).)*. Schließlich werden der Anfang und das Ende der Eingabe verankert, um sicherzustellen, dass die gesamte Eingabe verbraucht wird: ^((?!hede).)*$

Wie Sie sehen, schlägt die Eingabe "ABhedeCD" fehl, da bei e3 die Regex (?!hede) fehlschlägt (dort ist "hede" da vorne!).

5492
Bart Kiers

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

^(?!hede).*$

ist in der Regel viel effizienter als die Lösung zu enthält nicht "hede":

^((?!hede).)*$

Ersteres sucht nur an der ersten Position der Eingabezeichenfolge nach "hede", nicht an jeder Position.

689
JoshuaDavid

Wenn Sie nur für grep verwenden, können Sie mit grep -v hede alle Zeilen abrufen, die keine Hede enthalten.

ETA Oh, die Frage noch einmal lesen, grep -v ist wahrscheinlich das, was Sie unter "Optionen für Werkzeuge" verstanden haben.

178
Athena

Antworten:

^((?!hede).)*$

Erklärung:

^der Anfang der Zeichenfolge ( gruppieren und zu\1 erfassen (0 oder mehrmals (entspricht der größtmöglichen Anzahl)),
(?! schau voraus, ob es nicht gibt, 

hede Ihre Zeichenfolge, 

) Ende der Vorausschau, . jedes Zeichen außer\n,
)* end of\1 (Hinweis: Da Sie bei diesem Capture einen Quantifier verwenden, wird nur die LETZTE Wiederholung des erfassten Musters in\1 gespeichert.)
$ vor einem optionalen\n und dem Ende der Zeichenfolge

136
Jessica

Die gegebenen Antworten sind vollkommen in Ordnung, nur ein akademischer Punkt:

Reguläre Ausdrücke im Sinne der theoretischen Informatik SIND NICHT ABLEHNT mach es so. Für sie musste es so aussehen:

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

Dies entspricht nur einer vollständigen Übereinstimmung. Es wäre sogar umständlicher, wenn man es für untergeordnete Spiele machen würde.

94
Hades32

Wenn Sie möchten, dass der Regex-Test nur nach only fehlschlägt, wenn die gesamte Zeichenfolge übereinstimmt, funktioniert Folgendes:

^(?!hede$).*

z.B. - Wenn Sie alle Werte außer "foo" zulassen möchten (d. H. "Foofoo", "barfoo" und "foobar" werden bestanden, aber "foo" schlägt fehl), verwenden Sie: ^(?!foo$).*

Wenn Sie nach der exact - Gleichheit suchen, ist es in diesem Fall eine bessere allgemeine Lösung, auf die Gleichheit von Zeichenfolgen zu prüfen, d. H. 

myStr !== 'foo'

Sie können sogar die Negation außerhalb des Tests setzen, wenn Sie Regex-Funktionen benötigen (hier Groß-/Kleinschreibung und Bereichsanpassung):

!/^[a-f]oo$/i.test(myStr)

Die Regex-Lösung oben in dieser Antwort kann jedoch in Situationen hilfreich sein, in denen ein positiver Regex-Test erforderlich ist (möglicherweise durch eine API).

52
Roy Tinker

Hier ist eine gute Erklärung warum es nicht einfach ist, einen willkürlichen Regex zu negieren. Ich muss jedoch den anderen Antworten zustimmen: Wenn dies etwas anderes als eine hypothetische Frage ist, ist eine Regex hier nicht die richtige Wahl.

50
Josh Lee

FWIW: Da reguläre Sprachen (auch als "rational" bezeichnete Sprachen) unter Komplementierung geschlossen werden, ist es immer möglich, einen regulären Ausdruck (auch als "rationaler Ausdruck" bezeichnet) zu finden, der einen anderen Ausdruck negiert. Dies wird jedoch nicht von vielen Tools implementiert.

Vcsn unterstützt diesen Operator (was {c}, postfix bedeutet).

Sie definieren zuerst den Typ Ihrer Ausdrücke: Beschriftungen sind Buchstaben (lal_char), die zum Beispiel von a bis z ausgewählt werden können (die Definition des Alphabets bei der Arbeit mit Komplementierung ist natürlich sehr wichtig), und der für jedes Word berechnete Wert ist nur ein boolescher: true das Wort wird akzeptiert, false, abgelehnt.

In Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → ????

dann geben Sie Ihren Ausdruck ein:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

wandeln Sie diesen Ausdruck in einen Automaten um:

In [7]: a = e.automaton(); a

 The corresponding automaton

konvertieren Sie schließlich diesen Automaten wieder in einen einfachen Ausdruck.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

wenn + normalerweise als | bezeichnet wird, bezeichnet \e das leere Wort und [^] wird normalerweise . (beliebiges Zeichen) geschrieben. Also, mit etwas Umschreiben von ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Sie können dieses Beispiel hier sehen und Vcsn online dort ausprobieren .

49
akim

Benchmarks

Ich entschied mich dazu, einige der vorgestellten Optionen auszuwerten und deren Leistung zu vergleichen sowie einige neue Funktionen zu verwenden. Benchmarking für .NET Regex Engine: http://regexhero.net/tester/

Benchmark-Text:

Die ersten 7 Zeilen sollten nicht übereinstimmen, da sie den gesuchten Ausdruck enthalten, während die unteren 7 Zeilen übereinstimmen sollten!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Ergebnisse:

Ergebnisse sind Iterationen pro Sekunde als Mittelwert von 3 Durchläufen - Bigger Number = Better

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the Word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Da .NET keine Aktionsverben unterstützt (* FAIL usw.), konnte ich die Lösungen P1 und P2 nicht testen.

Zusammenfassung:

Ich habe versucht, die meisten vorgeschlagenen Lösungen zu testen, einige Optimierungen sind für bestimmte Wörter möglich. Wenn beispielsweise die ersten beiden Buchstaben der Suchzeichenfolge nicht gleich sind, kann Antwort 03 zu ^(?>[^R]+|R+(?!egex Hero))*$ erweitert werden, was zu einer geringen Leistung führt gewinnen.

Die allgemein lesbarste und leistungsstärkste Lösung scheint jedoch 05 zu sein, wobei eine Bedingungsanweisung Oder 04 mit dem Possesiv-Quantifizierer verwendet wird. Ich denke, die Perl-Lösungen sollten noch schneller und besser lesbar sein.

41
Falco

Mit negativem Lookahead können reguläre Ausdrücke übereinstimmen, die kein bestimmtes Muster enthalten. Dies wird von Bart Kiers beantwortet und erklärt. Großartige erklärung!

Mit der Antwort von Bart Kiers testet der Lookahead-Teil 1 bis 4 Zeichen weiter, während er mit jedem einzelnen Zeichen übereinstimmt. Wir können dies vermeiden und den Lookahead-Teil den gesamten Text überprüfen lassen, um sicherzustellen, dass es keine "Hede" gibt. Dann kann der normale Teil (. *) Den gesamten Text auf einmal verzehren.

Hier ist der verbesserte Regex:

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

Beachten Sie, dass der Lazy-Quantifizierer (*?) Im negativen Lookahead-Bereich optional ist. Sie können stattdessen (*) einen gierigen Quantifizierer verwenden, abhängig von Ihren Daten: Wenn "hede" vorhanden ist und in der ersten Hälfte des Textes der Lazy-Quantifizierer angezeigt wird sei schneller; Andernfalls kann der gierige Quantifizierer schneller sein. Wenn jedoch "hede" nicht vorhanden ist, wären beide gleich langsam.

Hier ist der Demo Code .

Weitere Informationen zu Lookahead finden Sie im großartigen Artikel: Beherrschen von Lookahead und Lookbehind .

Schauen Sie sich auch die Datei RegexGen.js an, einen JavaScript-Generator für reguläre Ausdrücke, mit dem komplexe reguläre Ausdrücke erstellt werden können. Mit RegexGen.js können Sie die Regex besser lesbar konstruieren:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);
40
amobiz

Regex nicht, aber ich finde es logisch und nützlich, serielle Greps mit Pipe zu verwenden, um Rauschen zu vermeiden.

z.B. Suchen Sie in einer Apache-Konfigurationsdatei ohne alle Kommentare.

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

und

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

Die Logik der seriellen grep's ist (kein Kommentar) und (entspricht dir)

32
kiwalk

damit vermeiden Sie es, einen Lookahead auf jeder Position zu testen:

/^(?:[^h]+|h++(?!ede))*+$/

äquivalent zu (für .net):

^(?>(?:[^h]+|h+(?!ede))*)$

Alte Antwort:

/^(?>[^h]+|h+(?!ede))*$/
28

So würde ich es machen:

^[^h]*(h(?!ede)[^h]*)*$

Genau und effizienter als die anderen Antworten. Es implementiert Friedls "Abroll-the-Loop" Effizienz-Technik und erfordert viel weniger Rückverfolgung.

19
ridgerunner

Der oben erwähnte (?:(?!hede).)* ist großartig, weil er verankert werden kann.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

In diesem Fall würde jedoch Folgendes ausreichen:

^(?!.*hede)                    # A line without hede

Diese Vereinfachung ist bereit, um "AND" -Klauseln hinzuzufügen:

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same
18
ikegami

Wenn Sie mit einem Zeichen übereinstimmen möchten, um ein Word zu negieren, das einer Zeichenklasse ähnelt:

Zum Beispiel eine Zeichenfolge:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

Verwende nicht:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

Benutzen:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

Hinweis "(?!bbb)." ist weder LookHind noch Lookahead, es ist LookCurrent, zum Beispiel:

"(?=abc)abcde", "(?!abc)abcde"
17
diyism

Das OP hat oder nicht spezifiziert Tag Der Beitrag gibt den Kontext an (Programmiersprache, Editor, Tool), in dem der Regex verwendet wird.

Bei mir muss ich dies manchmal tun, während ich eine Datei mit Textpad bearbeite.

Textpad unterstützt einige Regex-Programme, jedoch kein Lookahead oder Lookbehind. Daher sind einige Schritte erforderlich.

Wenn ich alle Zeilen behalten will das Unterlassen Sie Enthält den String hede, würde ich so machen:

1. Suchen/Ersetzen Sie die gesamte Datei, um am Anfang jeder Zeile, die einen beliebigen Text enthält, ein eindeutiges "Tag" hinzuzufügen.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Löschen Sie alle Zeilen mit der Zeichenfolge hede (Ersatzzeichenfolge ist leer):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. An dieser Stelle alle verbleibenden Zeilen Unterlassen Sie Enthält den String hede. Entfernen Sie das eindeutige "Tag" aus allen Zeilen (Ersatzzeichenfolge ist leer):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Jetzt haben Sie den Originaltext mit allen Zeilen, die die Zeichenfolge hede enthalten, entfernt.


Wenn ich danach schaue Mach etwas anderes nur Zeilen, die Unterlassen Sie Enthält den String hede, würde ich so machen:

1. Suchen/Ersetzen Sie die gesamte Datei, um am Anfang jeder Zeile, die einen beliebigen Text enthält, ein eindeutiges "Tag" hinzuzufügen.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Entfernen Sie für alle Zeilen, die die Zeichenfolge hede enthalten, das eindeutige "Tag":

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. An dieser Stelle alle Zeilen, die mit dem eindeutigen "Tag" beginnen, Unterlassen Sie Enthält den String hede. Ich kann jetzt mein machen Etwas anderes nur zu diesen Zeilen.

4. Wenn ich fertig bin, entferne ich das eindeutige "Tag" aus allen Zeilen (die Ersatzzeichenfolge ist leer):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  
13
Kevin Fegan

Seit der Einführung von Ruby-2.4.1 können wir den neuen Abwesenden Operator in den regulären Ausdrücken von Ruby verwenden

vom offiziellen doc

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

In Ihrem Fall erledigt ^(?~hede)$ die Arbeit für Sie

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]
9
aelor

Durch PCRE Verb (*SKIP)(*F)

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

Dies würde die Zeile, die die genaue Zeichenfolge hede enthält, vollständig überspringen und mit allen verbleibenden Zeilen übereinstimmen.

DEMO

Ausführung der Teile:

Betrachten wir den obigen regulären Ausdruck, indem wir ihn in zwei Teile aufteilen.

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

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

    ^.*$
    

TEIL 1

Die Ausführung der Regex-Engine beginnt mit dem ersten Teil.

^hede$(*SKIP)(*F)

Erklärung:

  • ^ Stellt fest, dass wir am Start sind.
  • hede Entspricht der Zeichenfolge hede
  • $ Gibt an, dass wir uns am Zeilenende befinden.

Die Zeile, die den String hede enthält, würde also übereinstimmen. Sobald die Regex-Engine das folgende (*SKIP)(*F) ( Hinweis: Sie können (*F) Als (*FAIL)) Verb schreiben, überspringt sie und stellt die Übereinstimmung her Versagen. | Wird als Änderung oder als logischer OR Operator, der neben dem PCRE-Verb hinzugefügt wird, dessen Inturn mit allen Grenzen übereinstimmt, existiert zwischen jedem einzelnen Zeichen in allen Zeilen, mit Ausnahme der Zeile, die die genaue Zeichenfolge enthält hede. Siehe die Demo hier . Das heißt, es wird versucht, die Zeichen aus der verbleibenden Zeichenfolge abzugleichen. Nun würde der reguläre Ausdruck im zweiten Teil ausgeführt.

TEIL 2

^.*$

Erklärung:

  • ^ Stellt fest, dass wir am Start sind. Das heißt, es entspricht allen Zeilenanfängen mit Ausnahme der in der hede -Zeile. Siehe die Demo hier .
  • .* Im Mehrzeilenmodus würde . Mit jedem Zeichen außer Zeilenvorschub oder Zeilenumbruch übereinstimmen. Und * Würde das vorherige Zeichen null oder mehrmals wiederholen. Also würde .* Der gesamten Zeile entsprechen. Siehe die Demo hier .

    Hey, warum hast du. * Statt. + Hinzugefügt?

    Weil .* Mit einer Leerzeile übereinstimmen würde, aber .+ Nicht mit einer Leerzeile übereinstimmen würde. Wir möchten alle Zeilen mit Ausnahme von hede abgleichen. Möglicherweise enthält die Eingabe auch Leerzeilen. Sie müssen also .* anstelle von .+ verwenden. .+ Würde das vorherige Zeichen ein oder mehrere Male wiederholen. Siehe .* Stimmt mit einer Leerzeile überein hier .

  • $ Das Ende des Zeilenankers ist hier nicht erforderlich.

9
Avinash Raj

Da niemand sonst die Frage die gestellt wurde direkt beantwortet hat, werde ich es tun.

Die Antwort ist, dass es mit POSIX grep unmöglich ist, diese Anforderung buchstäblich zu erfüllen:

grep "Regex for doesn't contain hede" Input

Der Grund ist, dass POSIX grep nur mit Basic Regular Expressions arbeiten muss, die einfach nicht mächtig genug sind, um diese Aufgabe auszuführen (sie sind nicht in der Lage, reguläre Sprachen zu parsen, da keine Wechsel- und Gruppierungsmöglichkeiten vorhanden sind).

GNU grep implementiert jedoch Erweiterungen, die dies zulassen. Insbesondere ist \| der Wechseloperator in der GNU-Implementierung von BREs und \( und \) die Gruppierungsoperatoren. Wenn Ihre Engine für reguläre Ausdrücke Abwechslung, negative Klammerausdrücke, Gruppierung und den Kleene-Stern unterstützt und in der Lage ist, sich am Anfang und am Ende der Zeichenfolge zu verankern, ist dies alles, was Sie für diesen Ansatz benötigen.

Mit GNU grep würde es ungefähr so ​​aussehen:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" Input

(gefunden mit Grail und einigen weiteren Optimierungen von Hand).

Sie können auch ein Tool verwenden, das Extended Regular Expressions wie egrep implementiert, um die umgekehrten Schrägstriche zu beseitigen:

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" Input

Hier ist ein Skript zum Testen (beachten Sie, dass im aktuellen Verzeichnis eine Datei testinput.txt generiert wird):

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

In meinem System wird gedruckt:

Files /dev/fd/63 and /dev/fd/62 are identical

wie erwartet.

Für diejenigen, die an Details interessiert sind, besteht die Technik darin, den regulären Ausdruck, der dem Wort entspricht, in einen endlichen Automaten umzuwandeln, den Automaten umzukehren, indem er jeden Akzeptanzzustand in Nichtakzeptanz und umgekehrt umwandelt und dann den resultierenden FA zurück in konvertiert ein regulärer Ausdruck.

Wie alle bemerkt haben, vereinfacht dies die Aufgabe, wenn Ihre Engine für reguläre Ausdrücke einen negativen Lookahead unterstützt. Zum Beispiel mit GNU grep:

grep -P '^((?!hede).)*$' Input

Update: Ich habe kürzlich die hervorragende FormalTheory - Bibliothek von Kendall Hopkins gefunden, die in PHP geschrieben ist und eine ähnliche Funktionalität wie Grail bietet. Mit diesem und einem von mir selbst erstellten Vereinfacher konnte ich einen Online-Generator für negative reguläre Ausdrücke erstellen, wenn eine Eingabesatz eingegeben wurde (derzeit werden nur alphanumerische Zeichen und Leerzeichen unterstützt): http://www.formauri.es/ personal/pgimeno/misc/non-match-regex/

Für hede wird ausgegeben:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

was dem oben genannten entspricht.

8
Pedro Gimeno

Es kann einfacher sein, zwei Ausdrücke in Ihrem Code zu pflegen, eine für die erste Übereinstimmung, und dann, wenn sie übereinstimmt, führen Sie die zweite Regex aus, um Ausfälle zu prüfen, die Sie beispielsweise ^.*(hede).* blockieren möchten, und dann die entsprechende Logik in Ihrem Code.

OK, ich gebe zu, dass dies nicht wirklich eine Antwort auf die gepostete Frage ist und auch etwas mehr Verarbeitung als ein einzelner Regex benötigt. Aber für Entwickler, die auf der Suche nach einer schnellen Notfalllösung für einen Ausreißerfall kamen, sollte diese Lösung nicht übersehen werden.

6
andrew pate

Die TXR Language unterstützt Regex-Negation.

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

Ein komplizierteres Beispiel: Ordnen Sie alle Zeilen zu, die mit a beginnen und mit z enden, enthalten jedoch nicht die Teilzeichenfolge hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

Die Regex-Negation ist an sich nicht besonders nützlich, aber wenn Sie auch eine Kreuzung haben, werden die Dinge interessant, da Sie über einen vollständigen Satz boolescher Mengenoperationen verfügen: Sie können "den Satz, der diesem entspricht, ausdrücken, mit Ausnahme der Dinge, die dem entsprechen".

5
Kaz

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

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

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

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>
3
Daniel Nyamasyo

Eine meiner Meinung nach besser lesbare Variante der Top-Antwort: 

^(?!.*hede)

Grundsätzlich gilt: "Stimmt am Anfang der Zeile überein, und nur dann, wenn sie nicht" hede "enthält" - so wurde die Anforderung fast direkt in Regex übersetzt.

Natürlich können mehrere Fehleranforderungen vorliegen:

^(?!.*(hede|hodo|hada))

Details: Der Anker ^ sorgt dafür, dass die Regex-Engine die Übereinstimmung nicht an jeder Stelle in der Zeichenfolge wiederholt, was mit jeder Zeichenfolge übereinstimmen würde.

Der ^ Anker am Anfang soll den Anfang der Zeile darstellen. Das grep-Tool passt jede Zeile einzeln an. In Kontexten, in denen Sie mit einer mehrzeiligen Zeichenfolge arbeiten, können Sie das Flag "m" verwenden:

/^(?!.*hede)/m # JavaScript syntax

oder

(?m)^(?!.*hede) # Inline flag
3
staafl

Eine andere Möglichkeit ist, einen positiven Look-Ahead hinzuzufügen und zu prüfen, ob hehe irgendwo in der Eingabezeile steht. Dann würden wir dies mit einem Ausdruck annullieren, der ähnlich dem folgenden ist:

^(?!(?=.*\bhede\b)).*$

mit Wortgrenzen.


Der Ausdruck wird oben rechts in regex101.com erklärt, wenn Sie ihn untersuchen/vereinfachen/ändern möchten, und in dieser Link können Sie sehen, wie er aussehen würde Wenn Sie möchten, können Sie eine Übereinstimmung mit einigen Sample-Eingaben erzielen.


RegEx-Schaltung

jex.im visualisiert reguläre Ausdrücke:

enter image description here

1
Emma

Mit ConyEdit können Sie die Befehlszeile cc.gl !/hede/ verwenden, um Zeilen abzurufen, die nicht den regulären Ausdruck enthalten, oder die Befehlszeile cc.dl /hede/ verwenden, um Zeilen zu löschen, die den regulären Ausdruck enthalten. Sie haben das gleiche Ergebnis.

0
Donald

Vielleicht finden Sie dies bei Google, während Sie versuchen, einen Regex zu schreiben, der in der Lage ist, Segmente einer Zeile (im Gegensatz zu ganzen Zeilen) abzugleichen, die nicht einen Teilstring enthalten. Habe mich eine Weile ausfindig gemacht, also werde ich sagen:

Gegeben eine Zeichenfolge: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Ich möchte <span>-Tags abgleichen, die den Teilstring "bad" nicht enthalten.

/<span(?:(?!bad).)*?> stimmt mit <span class=\"good\"> und <span class=\"ugly\"> überein.

Beachten Sie, dass es zwei Sätze (Ebenen) von Klammern gibt:

  • Der innerste ist für den negativen Lookahead (es ist keine Fanggruppe)
  • Das Äußerste wurde von Ruby als Capture-Gruppe interpretiert, aber wir möchten nicht, dass es sich um eine Capture-Gruppe handelt. Ich fügte hinzu:? Am Anfang und nicht mehr als Capture-Gruppe.

Demo in Ruby:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]
0
BrunoFacca

So verwenden Sie die Rückverfolgungssteuerungsverben von PCRE, um eine Zeile zu finden, die kein Word enthält

Hier ist eine Methode, die ich noch nicht gesehen habe:

/.*hede(*COMMIT)^|/

Wie es funktioniert

Erstens versucht es irgendwo in der Zeile "hede" zu finden. Wenn dies der Fall ist, teilt (*COMMIT) der Engine mit, dass sie im Fehlerfall nicht nur zurückverfolgt werden soll, sondern auch in diesem Fall keine weiteren Übereinstimmungen versuchen soll. Dann versuchen wir, etwas zu finden, das möglicherweise nicht passt (in diesem Fall ^).

Wenn eine Zeile nicht "hede" enthält, stimmt die zweite Alternative, ein leeres Submuster, erfolgreich mit der Betreffzeichenfolge überein.

Diese Methode ist nicht effizienter als ein negativer Lookahead, aber ich dachte mir, ich würde sie einfach hier werfen, falls jemand sie geschickt findet und eine Verwendung für andere, interessantere Anwendungen findet.

0
jaytea

^ ((?! hede).) * $ ist eine elegante Lösung, außer da sie Zeichen enthält, können Sie sie nicht mit anderen Kriterien kombinieren. Angenommen, Sie möchten überprüfen, ob "hede" und "haha" nicht vorhanden sind. Diese Lösung würde funktionieren, da keine Zeichen verbraucht werden:

^ (?! .\bhede\b) (? =. \ bhaha\b)

0

Eine einfachere Lösung ist die Verwendung des nicht-Operators!

Ihre if -Anweisung muss mit "contain" und nicht mit "excluded" übereinstimmen.

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

Ich glaube, die Designer von RegEx haben die Verwendung von Nicht-Operatoren vorweggenommen.

0
JohnP2