Frage Regulärer Ausdruck, um einer Zeile zu entsprechen, die kein Wort enthält?


Ich weiß, dass es möglich ist, ein Wort abzugleichen und die Übereinstimmungen dann mit anderen Werkzeugen umzukehren (z.B. grep -v). Ich würde jedoch gerne wissen, ob es möglich ist, die Zeilen so anzupassen nicht ein spezifisches Wort (z.B. hede) unter Verwendung eines regulären Ausdrucks enthalten.

Eingang:

hoho
hihi
haha
hede

Code:

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

Gewünschte Ausgabe:

hoho
hihi
haha

3559


Ursprung


Antworten:


Die Vorstellung, dass Regex die inverse Übereinstimmung nicht unterstützt, ist nicht vollständig richtig. Sie können dieses Verhalten mit negativen Look-arounds nachahmen:

^((?!hede).)*$

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

Und wenn Sie auch Zeilenumbruchzeichen benötigen, verwenden Sie die DOT-ALL-Modifikator (das Nachlaufende s im folgenden Muster):

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

oder benutze es inline:

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

(bei dem die /.../ sind die Regex-Begrenzer, d. h. nicht Teil des Musters

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

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

Erläuterung

Eine Zeichenfolge ist nur eine Liste von n Figuren. Vor und nach jedem Zeichen gibt es eine leere Zeichenfolge. Also eine Liste von n Charaktere werden haben 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

bei dem die eEs sind die leeren Saiten. Die Regex (?!hede). schaut nach vorne, um zu sehen, ob es keine Teilkette gibt "hede" gesehen werden, und wenn das der Fall ist (also etwas anderes gesehen wird), dann das . (Punkt) wird mit jedem Zeichen außer einem Zeilenumbruch übereinstimmen. Look-arounds werden auch genannt Zero-Weiten-Assertions weil sie es nicht tun verbrauchen irgendwelche Charaktere. Sie behaupten / bestätigen nur etwas.

In meinem Beispiel wird also jede leere Zeichenfolge zuerst überprüft, um zu sehen, ob es keine gibt "hede" im Voraus, bevor ein Charakter von der verbraucht wird . (Punkt). Die Regex (?!hede). wird das nur einmal tun, also wird es in eine Gruppe gehüllt und null oder mehrmals wiederholt: ((?!hede).)*. Schließlich werden der Anfang und das Ende der Eingabe verankert, um sicherzustellen, dass die gesamte Eingabe konsumiert wird: ^((?!hede).)*$

Wie Sie sehen können, die Eingabe "ABhedeCD" wird scheitern, weil auf e3, die Regex (?!hede) scheitert (dort ist  "hede" da vorne!).


4852



Beachten Sie, dass die Lösung für nicht beginnen mit "Hede":

^(?!hede).*$

ist im Allgemeinen viel effizienter als die Lösung zu nicht enthalten "Hede":

^((?!hede).)*$

Das erstere sucht nach "hede" nur an der ersten Position der Eingabestring und nicht an jeder Position.


603



Ob du benutzt es nur für Grep, du kannst es benutzen grep -v hede um alle Zeilen zu erhalten, die keine hede enthalten.

ETA Oh, die Frage noch einmal gelesen, grep -v ist wahrscheinlich, was du mit "Werkzeugoptionen" meintest.


163



Antworten:

^((?!hede).)*$

Erläuterung:

^der Anfang der Saite, ( Gruppe und Capture zu \ 1 (0 oder mehr Male (entspricht der größtmöglichen Menge)),
(?! schaue voraus, um zu sehen, ob es nicht ist,

hededeine Schnur,

) Ende der Vorausschau, . ein beliebiges Zeichen außer \ n,
)* Ende von \ 1 (Hinweis: Da Sie bei dieser Aufnahme einen Quantifizierer verwenden, wird nur die LETZTE Wiederholung des aufgenommenen Musters in \ 1 gespeichert)
$ vor einem optionalen \ n und dem Ende der Zeichenfolge


121



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

Reguläre Ausdrücke im Sinne der theoretischen Informatik SIND NICHT IN DER LAGE mach es so. Für sie musste es in etwa so aussehen:

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

Dies ist nur eine vollständige Übereinstimmung. Für Sub-Matches zu tun wäre sogar noch peinlicher.


89



Wenn Sie den Regex-Test durchführen möchten nur scheitern wenn die gesamte Zeichenfolge Übereinstimmungen, das Folgende wird funktionieren:

^(?!hede$).*

z.B. - Wenn Sie alle Werte außer "foo" zulassen möchten (d. H. "Foofoo", "barfoo" und "foobar" werden übergeben, aber "foo" wird fehlschlagen), verwenden Sie: ^(?!foo$).*

Natürlich, wenn Sie nach etwas suchen genau Gleichheit ist eine bessere allgemeine Lösung in diesem Fall, die Gleichheit der Zeichenkette zu prüfen, d.h.

myStr !== 'foo'

Sie könnten sogar die Negation stellen draußen der Test, wenn Sie irgendwelche Regex-Funktionen benötigen (hier: Groß- / Kleinschreibung und Bereichsanpassung):

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

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


48



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


47



FWIW, da reguläre Sprachen (auch rationale Sprachen genannt) bei der Ergänzung geschlossen werden, ist es immer möglich, einen regulären Ausdruck (auch rationaler Ausdruck genannt) zu finden, der einen anderen Ausdruck negiert. Aber nicht viele Tools implementieren dies.

Vcsn unterstützt diesen Operator (was er bezeichnet {c}, Postfix).

Sie definieren zuerst den Typ Ihrer Ausdrücke: Beschriftungen sind Buchstaben (lal_char) aus wählen a zu z zum Beispiel (das Alphabet zu definieren, wenn mit der Komplementierung gearbeitet wird, ist natürlich sehr wichtig), und der "Wert", der für jedes Wort berechnet wird, ist nur ein Boolescher Wert: true das Wort ist angenommen, 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 gibst du deinen Ausdruck ein:

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

wandle diesen Ausdruck in einen Automaten um:

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

The corresponding automaton

schließlich, wandle diesen Automaten wieder in einen einfachen Ausdruck um.

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

woher + wird normalerweise bezeichnet |, \e bezeichnet das leere Wort und [^] wird normalerweise geschrieben . (irgendein Zeichen). Also, mit ein bisschen Umschreiben ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Sie können dieses Beispiel sehen Hier, und versuchen Sie Vcsn online Dort.


43



Benchmarks

Ich entschied mich, einige der vorgestellten Optionen zu bewerten und ihre Leistung zu vergleichen, sowie einige neue Features 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, wenn der Median von 3 Läufen - Größere Anzahl = Besser

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 (* FAIL, etc.) unterstützt, 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 zum Beispiel die ersten beiden Buchstaben des Suchstrings nicht identisch sind, kann die Antwort 03 auf erweitert werden ^(?>[^R]+|R+(?!egex Hero))*$ was zu einem kleinen Leistungsgewinn führt.

Die am besten lesbare und leistungsmäßig schnellste Lösung scheint jedoch eine bedingte Anweisung zu sein oder 04 mit dem Besitzquantor. Ich denke, die Perl-Lösungen sollten noch schneller und leichter lesbar sein.


38



Bei negativem Lookahead kann regulärer Ausdruck mit etwas übereinstimmen, das kein spezifisches Muster enthält. Dies wird von Bart Kiers beantwortet und erklärt. Tolle Erklärung!

Mit der Antwort von Bart Kier wird der Lookahead-Teil jedoch 1 bis 4 Zeichen vorausgehen, während er ein beliebiges einzelnes Zeichen abgleicht. Wir können das vermeiden und den Lookahead-Teil den ganzen Text auschecken lassen, sicherstellen, dass es kein 'hede' gibt, und dann kann der normale Teil (. *) Den ganzen Text auf einmal essen.

Hier ist der verbesserte Regex:

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

Beachten Sie, dass der (*?) Lazy-Quantor im negativen Lookahead-Teil optional ist, Sie können stattdessen (+) greedy quantifier verwenden, abhängig von Ihren Daten: Wenn 'hede' vorhanden ist und am Anfang die Hälfte des Textes, kann der Lazy-Quantor sei schneller; Sonst ist der gierige Quantifizierer schneller. Wenn 'hede' jedoch nicht vorhanden ist, wären beide gleich langsam.

Hier ist das Democode.

Weitere Informationen zu Lookahead finden Sie in dem tollen Artikel: Lookahead und Lookbehind beherrschen.

Bitte auch auschecken RegexGen.js, ein 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 auf eine lesbarere 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()
);

37



Nicht Regex, aber ich fand es logisch und nützlich, serielle Greps mit Pipe zu verwenden, um Rauschen zu beseitigen.

z.B. 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 von seriellen Grep's ist (kein Kommentar) und (entspricht Dir)


30