Frage

Ich weiß, es ist möglich, ein Wort zu passen und dann die Spiele Reverse mit anderen Werkzeugen (z grep -v). Allerdings ist es möglich, Linien entsprechen, die nicht ein bestimmtes Wort enthalten, z.B. hede, einen regulären Ausdruck?

Input:

hoho
hihi
haha
hede

Code:

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

Gewünschte Ausgabe:

hoho
hihi
haha
War es hilfreich?

Lösung

Die Vorstellung, dass nicht unterstützt inverses Matching regex ist nicht ganz richtig. Sie können dieses Verhalten nachahmen, indem Sie negativen Look-arounds:

^((?!hede).)*$

Die Regex oben wird jeden String oder Zeile ohne Zeilenumbruch, nicht mit dem (Teil-) Zeichenkette 'hede'. Wie bereits erwähnt, ist dies nicht etwas regex „gut“ an (oder tun sollte), aber immer noch, es ist möglich.

Und wenn Sie Linie übereinstimmen Zeichen brechen auch, verwenden Sie die DOT-ALL Modifikator (der hintere s in folgendem Muster):

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

oder verwenden Sie es inline:

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

(wo die /.../ sind die regex Trennzeichen, das heißt, nicht Teil des Musters)

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

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

Erklärung

Eine Zeichenfolge ist nur eine Liste von n Zeichen. Vor und nach jedem Zeichen, es ist eine leere Zeichenfolge. So eine Liste von n Zeichen haben wird leere Strings n+1. 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

, wo die e die die leeren Saiten. Die Regex (?!hede). schaut nach vorn zu sehen, ob es kein Teil "hede" ist zu sehen, und wenn dies der Fall ist (so etwas anderes zu sehen ist), dann ist die . (dot) wird jedes Zeichen außer einem Zeilenumbruch entsprechen. Look-arounds werden auch als Null-Breite-Behauptungen , weil sie es nicht tun verbrauchen alle Zeichen. Sie nur behaupten / validieren etwas.

Also, in meinem Beispiel wird jede leere Zeichenkette zuerst zu sehen, validiert, wenn es keine "hede" ist weiter vorne, bevor ein Zeichen durch die . (dot) verbraucht. Die Regex (?!hede). tun wird, dass nur ein einziges Mal, so ist es in einer Gruppe eingewickelt, und wiederholte null oder mehr Male: ((?!hede).)*. Schließlich werden die Start- und End-of-Eingang verankert sicherstellen, dass die gesamte Eingabe verbraucht wird: ^((?!hede).)*$

Wie Sie sehen können, der Eingang "ABhedeCD" fehl, weil auf e3, der Regex (?!hede) versagt (es ist "hede" weiter vorne!).

Andere Tipps

Beachten Sie, dass die Lösung auf nicht beginnt mit „Hede“ :

^(?!hede).*$

ist in der Regel wesentlich effizienter als die Lösung auf nicht enthalten „Hede“ :

^((?!hede).)*$

Die früheren Kontrollen für „Hede“ nur an dem ersten Position des Eingabestring, anstatt an jeder Position.

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

ETA Oh, die Frage rereading, grep -v ist wahrscheinlich das, was Sie mit "Tool Optionen" zu verstehen.

Antwort:

^((?!hede).)*$

Erklärung:

^the Anfang der Zeichenfolge, ( Gruppe und die Sicherung \ 1 (0 oder mehrmals (passend die meisten Menge möglich)),
(?! nach vorne schauen, um zu sehen, ob es nicht,

hede Zeichenfolge,

) Ende Vorgriff, . alle Zeichen außer \ n,
)* Ende \ 1 (Anmerkung: weil Sie einen Quantifizierer auf dieser Erfassung verwenden, nur die letzte Wiederholung des erfassten Muster in \ 1 gespeichert wird)
$ vor einem optionalen \ n, und das Ende der Zeichenfolge

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

Reguläre Ausdrücke im Sinne der theoretischen Informatik nicht in der Lage tut es wie folgt. Für sie hatte es so etwas wie folgt aussehen:

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

Dies nur, dass eine vollständige Übereinstimmung. es für die Unter Streichhölzer tun noch mehr umständlich wäre.

Wenn Sie den regex Test auf nur fehlschlagen, wenn die gesamte Zeichenfolge übereinstimmt, wird folgende Arbeiten:

^(?!hede$).*

z. - Wenn Sie alle Werte mit Ausnahme von "foo" (das heißt "foofoo", "barfoo" und "foobar" wird passieren, aber "foo" fehl) zu ermöglichen, verwenden Sie: ^(?!foo$).*

Natürlich, wenn Sie überprüfen für genau Gleichheit, eine bessere allgemeine Lösung ist in diesem Fall für die Zeichenfolge Gleichheit zu überprüfen, das heißt

myStr !== 'foo'

Sie auch die Negation setzen könnten außerhalb den Test, wenn Sie irgendwelche Regex Funktionen benötigen (hier Groß- und Kleinschreibung und Bereichsanpassung):

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

Die Regex-Lösung an der Spitze dieser Antwort kann hilfreich sein, in Situationen jedoch, wo ein positiver regex Test erforderlich ist (vielleicht durch eine API).

FWIW, da reguläre Sprachen (auch bekannt als rationale Sprachen) unter Komplementierung geschlossen sind, ist es immer möglich, einen regulären Ausdruck zu finden (auch bekannt als rationaler Ausdruck), der einen anderen Ausdruck negiert. Aber nicht viele Werkzeuge, um dies umzusetzen.

VCSN diesen Operator unterstützt (was es bedeutet {c}, Postfix).

Sie zuerst die Art Ihrer Ausdrücke definieren: Etiketten sind Buchstaben (lal_char) von a holen zum Beispiel z (Definition des Alphabets, wenn sie mit Komplementierung arbeiten, ist natürlich sehr wichtig), und der „Wert“ berechnet für jedes Wort ist nur ein Boolean. true das Wort akzeptiert wird, 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:

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

konvertiert diesen Ausdruck auf einen Automaten:

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

 Der entsprechende Automat

Schließlich wandelt diese Automaten zurück zu einem einfachen Ausdruck.

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

, wobei in der Regel + | bezeichnet wird, bezeichnet \e das leere Wort, und ist in der Regel [^] . (alle Zeichen) geschrieben. Also, mit einem bisschen Umschreiben ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Sie sehen dieses Beispiel enthält hier und versuchen VCSN Online es .

Hier eine gute Erklärung , warum es ist nicht einfach eine beliebige regex zu negieren. Ich habe mit den anderen Antworten zustimmen, aber: wenn dies etwas anderes als eine hypothetische Frage ist, dann ein regulärer Ausdruck ist nicht die richtige Wahl hier

.

Mit negativer Vorschau kann regulärer Ausdruck etwas nicht spezifische Muster enthält. Dies wird beantwortet und erklärt von Bart Kiers. Große Erklärung!

Doch mit Bart Kiers' Antwort, wird der Look-Ahead-Teil testen 1 bis 4 Zeichen voraus, während ein einzelnes Zeichen übereinstimmen. Wir können dies vermeiden, und lassen Sie den Look-Ahead-Teil den ganzen Text überprüfen, sicherzustellen, dass es keinen ‚Hede‘, und dann der normale Teil (. *) Den ganzen Text essen können alle auf einmal.

Hier ist die verbesserte regex:

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

Beachten Sie die faulen Quantifizierer im negativen Look-Ahead-Teil optional sind, können Sie mit (*) gierig quantifier statt, abhängig von Ihren Daten (*?): Wenn ‚hede‘ tut Gegenwart und in der Anfangs Hälfte des Textes, die faul quantifier kann schneller sein; andernfalls wird das gierige Quantor schneller. Allerdings, wenn ‚hede‘ nicht vorhanden tut, würden beide gleich langsam sein.

Hier ist der Demo-Code .

Weitere Informationen über die Look-Ahead finden Sie in der großen Artikel finden Sie unter:. Mastering und Lookahead Lookbehind

Auch lesen Sie bitte RegexGen.js , einen JavaScript-Regular Expression Generator, die komplexe reguläre Ausdrücke zu konstruieren hilft. Mit RegexGen.js, können Sie die Regex in einer besser lesbaren Art und Weise 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()
);

Benchmarks

ich beschlossen, einige der vorgestellten Optionen zu bewerten und ihre Leistung vergleichen zu können, sowie einige neue Funktionen nutzen. Regex-Engine Benchmarking auf .NET: http://regexhero.net/tester/

Benchmark Text:

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

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:

Die Ergebnisse sind Iterationen pro Sekunde als Median von 3 Läufen - Bigger Number = Bessere

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 unterstützt keine Aktion Verb (* FAIL, etc.) Ich konnte die Lösungen P1 und P2 nicht testen.

Zusammenfassung:

Ich habe versucht, die meisten vorgeschlagenen Lösungen zu testen, sind einige Optimierungen möglich, bestimmte Wörter. Zum Beispiel, wenn die ersten beiden Buchstaben des Suchbegriffs nicht gleich sind, antworten 03 erweitert werden kann, um ^(?>[^R]+|R+(?!egex Hero))*$ in einer kleinen Leistungssteigerung zur Folge hat.

Aber das Gesamtbild am besten lesen und leistungsmäßig schnellste Lösung scheint 05 mit einer bedingten Anweisung zu sein oder 04 mit dem besitzergreifendere quantifier. Ich denke, die Perl-Lösungen sollten noch schneller und leichter lesbar sein.

Nicht regex, aber ich gefunden habe, ist es logisch und nützlich seriellen greps mit Rohr zu verwenden, Rauschen zu eliminieren.

zB. suche eine 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 ist (kein Kommentar) und (entspricht dir)

Damit vermeiden Sie an jeder Position einen Look-Ahead zu testen:

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

entspricht (für .net):

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

Alte Antwort:

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

Vorgenannte (?:(?!hede).)* ist groß, weil es verankert werden kann.

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

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

Aber die folgende würde in diesem Fall genügen:

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

Diese Vereinfachung ist bereit zu haben "AND" Klauseln hinzugefügt:

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

Hier ist, wie ich es tun würde:

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

Eine genaue und effizienter als die anderen Antworten. Es implementiert Friedl "Abrollen-the-Loop" Effizienz-Technik und viel weniger Rückzieher erfordert.

Wenn Sie ein Zeichen zu finden, ein Wort zu negieren ähnliche Zeichenklassen negieren:

Zum Beispiel kann eine Zeichenfolge:

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

Sie nicht verwenden:

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

Verwendung:

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

Hinweis "(?!bbb)." ist weder noch Lookbehind Look-Ahead, es ist lookcurrent, zum Beispiel:

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

Die OP hat angeben oder nicht Tag die Post den Kontext (Programmiersprache, Editor, Werkzeug), um anzuzeigen, die Regex innerhalb verwendet werden.

Für mich, ich brauche manchmal während der Bearbeitung einer Datei zu tun mit Textpad .

Textpad unterstützt einige Regex, aber nicht Look-Ahead oder Lookbehind unterstützen, so dauert es ein paar Schritte.

Wenn ich alle Zeilen zu halten suchen, dass Do not enthalten die Zeichenfolge hede , würde ich es tun, wie folgt:

  

1. Suche / Ersetzen die gesamte Datei einen eindeutigen „Tag“ an den Anfang jeder Zeile eines beliebigen Text enthält, hinzuzufügen.

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

2. Löschen Sie alle Zeilen, die die Zeichenfolge hede (Ersatz-String leer ist) enthalten:

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

  

3. An diesem Punkt sind alle übrigen Zeilen Do not enthalten die Zeichenfolge hede . Entfernen Sie den einzigartigen „Tag“ aus allen Zeilen (Ersatz-String leer ist):

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

Jetzt haben Sie den ursprünglichen Text mit allen Linien, die die Zeichenfolge mit hede entfernt.


Wenn ich bin auf der Suche nach Do Something Else , um nur Linien, die Do not enthalten die Zeichenfolge hede , ich es so tun würde:

  

1. Suche / Ersetzen die gesamte Datei einen eindeutigen „Tag“ an den Anfang jeder Zeile eines beliebigen Text enthält, hinzuzufügen.

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

2. Für alle Linien, die die Zeichenfolge hede , entfernen Sie den einzigartigen "Tag" enthalten:

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

  

3. An diesem Punkt werden alle Zeilen, die beginnen mit dem einzigartigen "Tag", Do not enthalten die Zeichenfolge hede . Ich kann jetzt tun, um mein Something Else , um nur diese Richtung.

  

4. Wenn ich fertig bin, entferne ich den einzigartigen „Tag“ aus allen Zeilen (Ersatz-String leer ist):

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

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

^(?!.*hede)

Im Grunde genommen „Spiel am Anfang der Zeile, wenn und nur dann, wenn sie nicht über‚Hede‘drin.“ - so die Forderung, fast direkt in regex übersetzt

Natürlich ist es möglich, mehr Ausfall Anforderungen hat:

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

Details:. Der ^ Anker sorgt das Regex-Engine an jeder Stelle in der Zeichenfolge das Spiel nicht erneut versuchen, die jeden Zeichenfolge übereinstimmen würden

Die ^ Anker am Anfang soll den Anfang der Zeile darzustellen. Das grep-Tool entspricht jede Zeile einen nach dem anderen, in Kontexten, in denen Sie mit einem mehrzeiligen String arbeiten, können Sie den „m“ Flag verwenden:

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

oder

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

Seit der Einführung von rubin 2.4.1 können wir die neuen Abwesend Operator verwenden in Rubys Reguläre Ausdrücke

von der offiziellen doc

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

So in Ihrem Fall ^(?~hede)$ macht den Job für Sie

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

Durch PCRE Verb (*SKIP)(*F)

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

Das würde überspringt vollständig die Linie, die die exakte Zeichenfolge hede enthält und passt alle restlichen Zeilen.

DEMO

Die Ausführung der Teile:

Lassen Sie uns die oben regex betrachten, indem sie in zwei Teile aufgeteilt wird.

  1. Teil vor dem | Symbol. Teil nicht angepasst werden.

    ^hede$(*SKIP)(*F)
    
  2. Ein Teil nach dem | Symbol. Teil sollte angepasst werden.

    ^.*$
    

TEIL 1

Regex Motor wird seine Ausführung aus dem ersten Teil beginnen.

^hede$(*SKIP)(*F)

Erklärung:

  • ^ Behauptet, dass wir am Anfang sind.
  • hede Spiele der String hede
  • $ Behauptet, dass wir am Ende der Leitung sind.

So ist die Linie, die die Zeichenfolge enthält hede würde angepasst werden. Sobald der Regex-Engine sieht die folgende (*SKIP)(*F) ( Hinweis: Sie (*F) als (*FAIL) schreiben könnten ) Verb, überspringt sie und das Spiel machen scheitern. | genannt Veränderung oder logischer OR-Operator hinzugefügt neben dem PCRE Verb, das inturn alle Grenzen übereinstimmt existiert zwischen jedem Zeichen auf allen Linien außer der Linie, die die exakte Zeichenfolge hede enthält. Sehen Sie die Demo rel="noreferrer">. Das heißt, versucht er, die Zeichen aus der restlichen Zeichenfolge übereinstimmen. Nun ist die regex im zweiten Teil ausgeführt wird.

TEIL 2

^.*$

Erklärung:

  • ^ Behauptet, dass wir am Anfang sind. dh passt es die ganze Zeile beginnt mit Ausnahme der in der hede Linie. Sehen Sie die Demo hier .
  • .* Im Multiline-Modus . würde jedes Zeichen außer Newline oder Carriage Return Zeichen übereinstimmen. Und * würde das vorherige Zeichen null oder mehr Male wiederholen. So würde .* die ganze Linie entspricht. Sehen Sie die Demo hier .

    Hey, warum Sie hinzugefügt. * Statt. +?

    Da .* würde eine leere Zeile passen, aber .+ wird kein Leerzeichen entsprechen. Wir wollen die Linien alle außer hede übereinstimmen, kann es eine Möglichkeit von Leerzeilen werden auch in den Eingang. so müssen Sie .* statt .+ verwenden. .+ würde das vorherige Zeichen ein oder mehrere Male wiederholen. Siehe .* entspricht einer Leerzeile hier .

  • $ Ende der Linie Anker nicht notwendig ist, hier.

Da sonst niemand eine direkte Antwort auf die Frage gegeben hat , die gefragt wurde, , ich werde es tun.

Die Antwort ist, dass mit POSIX grep, es unmöglich ist, zu wörtlich diese Anforderung zu erfüllen:

grep "Regex for doesn't contain hede" Input

Der Grund dafür ist, dass POSIX grep nur mit Basis arbeiten erforderlich ist reguläre Ausdrücke , die sind einfach nicht stark genug für diese Aufgabe vollbringen (sie sind nicht in der Lage regelmäßig Sprachen parsen, weil der Mangel an Abwechslung und Gruppierung).

Allerdings GNU grep implementiert Erweiterungen, die es erlauben. Insbesondere ist \| der Wechsel Operator in GNU-Implementierung von BREs und \( und \) sind die Gruppierung Operator. Wenn Ihr Modul für reguläre Ausdrücke Wechsel unterstützt negative Klammerausdrücke, Gruppierung und die Kleene Stern und in der Lage, den Anfang und das Ende der Schnur zu verankern, das ist alles, was Sie für diesen Ansatz benötigen.

Mit GNU grep, wäre es so etwas wie:

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

(gefunden mit Gral und einige weitere von Hand gemacht Optimierungen).

Sie können auch ein Tool, das erweiterte reguläre Ausdrücke , wie egrep, loszuwerden, die Schrägstriche zu bekommen:

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

Hier ist ein Skript, um es zu testen (man beachte, es erzeugt eine Datei testinput.txt im aktuellen Verzeichnis):

#!/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 druckt:

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

wie erwartet.

Für interessieren die in den Details, die verwendete Technik ist mit dem regulären Ausdruck zu konvertieren, die das Wort in einen endlichen Automaten übereinstimmt, dann die Automaten umkehren, indem jede Annahmezustand Nichtannahme und umgekehrt zu ändern, und die Umwandlung dann den resultierenden FA zurück zu einem regulären Ausdruck.

Schließlich, wie jeder bemerkt hat, wenn Ihr Modul für reguläre Ausdrücke negative Vorschau unterstützt, dass vereinfacht die Aufgabe viel. Zum Beispiel mit GNU grep:

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

Update: Ich habe Kendall Hopkins' ausgezeichnet FormalTheory Bibliothek , in PHP geschrieben, die eine ähnliche Funktionalität wie Gral bietet. Mit ihm und ein Vereinfacher von mir geschrieben, ich habe in der Lage gewesen, einen Online-Generator von negativer regulärer Ausdrücke eines Eingangsphrase (nur alphanumerische Zeichen und Leerzeichen zur Zeit unterstützt) zu schreiben: http://www.formauri.es/personal/pgimeno/misc/non-match-regex/

Für hede gibt sie:

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

, die zu dem obigen entsprechen.

Es kann mehr wartbar sein, um zwei reguläre Ausdrücke in Ihrem Code, ein das erste Spiel zu tun, und dann, wenn es die zweite Regex laufen Spiele für Ausreißer Fällen Sie zum Beispiel ^.*(hede).* dann entsprechende Logik in Ihrem Code haben blockieren möchten überprüfen .

OK, ich gebe das ist nicht wirklich eine Antwort auf die Frage gepostet geschrieben und es kann auch etwas mehr Verarbeitungsleistung als eine einzelne Regex verwenden. Aber für Entwickler, die hierher kamen für einen schnellen Notfall fix Suche nach einem Ausreißer Fall ist, dann sollte diese Lösung nicht übersehen werden.

Die TXR Sprache regex Negation unterstützt.

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

Ein komplizierteres Beispiel: alle Linien entsprechen, die mit a mit z beginnen und enden, enthält jedoch nicht den Teil 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

Regex Negation ist nicht besonders nützlich für sich allein, aber wenn man auch Kreuzung haben, werden die Dinge interessant, da Sie einen vollständigen Satz von Booleschen Set-Operationen haben: Sie können „den Satz ausdrücken, die dieser übereinstimmt, mit Ausnahme von Dingen, die übereinstimmen, die “.

Die unten Funktion hilft Ihnen, Ihre gewünschte Ausgabe 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;
    }


?>

Wie PCRE des Rückzieher Steuer Verben verwenden, um eine Zeile nicht enthält, um ein Wort zu entsprechen

Hier ist eine Methode, die ich vorher noch nicht verwendet gesehen:

/.*hede(*COMMIT)^|/

Wie es funktioniert

Zuerst versucht es „Hede“ irgendwo in der Linie zu finden. Wenn dies gelingt, an diesem Punkt, (*COMMIT) den Motor erzählt, nicht nur nicht im Fall eines Ausfalls Rückzieher, aber auch keine weiteren Übereinstimmungen in diesem Fall zu versuchen. Dann versuchen wir, etwas zu entsprechen, die möglicherweise nicht entsprechen können (in diesem Fall ^).

Wenn eine Zeile nicht enthält „Hede“ dann die zweite Alternative, ein leeres Untermuster, erfolgreich entspricht die Zeichenkette.

Diese Methode ist nicht effizienter als eine negative Vorschau, aber ich dachte, ich nur hier auf werfen würde, falls jemand es raffiniert findet und findet eine Verwendung für sie für andere, interessante Anwendungen.

Vielleicht werden Sie diese auf Google finden bei dem Versuch, einen regulären Ausdruck zu schreiben, die in der Lage ist Segmente einer Linie entspricht (im Gegensatz zu ganzen Linien gegen), die Sie nicht eine Zeichenkette enthalten. Tooke mich eine Weile, um herauszufinden, also werde ich teilen:

einen String Gegeben: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Ich mag <span> Tags Übereinstimmen, die nicht den Teil „schlecht“ enthalten.

/<span(?:(?!bad).)*?> wird <span class=\"good\"> und <span class=\"ugly\"> entsprechen.

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

  • Die innerste ist für den negativen Look-Ahead (es ist keine Capture-Gruppe ist)
  • wurde Die äußerste von Ruby als Capture-Gruppe interpretiert, aber wir wollen es nicht eine Capture-Gruppe sein, so fügte ich hinzu: es fängt an, und es wird nicht mehr als Capture-Gruppe interpretiert
  • .

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\">"]

Eine einfachere Lösung ist es, den laufenden Betrieb auf

zu verwenden,

, wenn Aussage übereinstimmen müssen "enthält" und nicht "schließt" entsprechen.

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

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

ich glaube, die Designer von RegEx die Verwendung von nicht-Betreiber erwartet.

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

^ ((?! hede).) * $ Ist eine elegante Lösung, mit der Ausnahme, da es Zeichen verbraucht werden Sie nicht in der Lage, es mit anderen Kriterien zu kombinieren. Zum Beispiel, sagen Sie für das Nicht-Vorhandensein von „Hede“ und dem Vorhandensein von „haha.“ Überprüfen wollten Diese Lösung würde funktionieren, weil es keine Zeichen verbrauchen:

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top