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.

78voto

Roy Tinker Punkte 9976

Wenn Sie möchten, dass der Regex-Test nur fehlschlägt, wenn der gesamte String übereinstimmt, funktioniert folgendes:

^(?!hede$).*

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

Natürlich, wenn Sie eine exakte Gleichheit überprüfen, ist in diesem Fall eine bessere allgemeine Lösung, auf Gleichheit der Zeichenfolge zu prüfen, d.h.

myStr !== 'foo'

Sie könnten sogar die Verneinung außerhalb des Tests platzieren, wenn Sie bestimmte Regex-Features benötigen (hier: Groß-/Kleinschreibung und Bereichsabgleich):

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

Die Regex-Lösung am Anfang dieser Antwort kann hilfreich sein, wenn ein positiver Regex-Test erforderlich ist (vielleicht von einer API).

0 Stimmen

Was ist mit Leerzeichen am Ende? Zum Beispiel, wenn ich möchte, dass der Test fehlschlägt mit dem String " hede "?

0 Stimmen

@eagor Die \s Direktive passt auf ein einzelnes Leerzeichen Zeichen.

0 Stimmen

Danke, aber es ist mir nicht gelungen, das Regex zu aktualisieren, um dies zum Funktionieren zu bringen.

70voto

amobiz Punkte 1098

Mit negativem Lookahead kann ein regulärer Ausdruck etwas anpassen, das ein bestimmtes Muster nicht enthält. Das wird von Bart Kiers beantwortet und erklärt. Tolle Erklärung!

Jedoch testet der Lookahead-Teil in Bart Kiers' Antwort 1 bis 4 Zeichen voraus, während er jedes einzelne Zeichen abgleicht. Wir können dies vermeiden und den Lookahead-Teil den gesamten Text überprüfen lassen, sicherstellen, dass kein 'hede' vorhanden ist, und dann kann der normale Teil (.*) den gesamten Text auf einmal verarbeiten.

Hier ist der verbesserte reguläre Ausdruck:

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

Beachten Sie, dass der (*?) lazy Quantifizierer im negativen Lookahead-Teil optional ist. Sie können stattdessen den (*) greedy Quantifizierer verwenden, abhängig von Ihren Daten: Wenn 'hede' vorhanden ist und sich in der ersten Hälfte des Textes befindet, kann der lazy Quantifizierer schneller sein; andernfalls ist der greedy Quantifizierer schneller. Wenn jedoch 'hede' nicht vorhanden ist, sind beide gleich langsam.

Hier ist der Demo-Code.

Für weitere Informationen zum Lookahead lesen Sie bitte den großartigen Artikel: Lookahead und Lookbehind meistern.

Schauen Sie auch RegexGen.js an, ein JavaScript Regular Expression Generator, der hilft, komplexe reguläre Ausdrücke zu konstruieren. Mit RegexGen.js können Sie den regulären Ausdruck auf eine lesbarere Weise konstruieren:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // alles anpassen, das nicht enthält:
        _.anything().lazy(), 'hede' //   null oder mehr Zeichen, die von 'hede' gefolgt werden,
                                    //   d.h., alles, was 'hede' enthält
    ), 
    _.endOfLine()
);

4 Stimmen

So um einfach zu überprüfen, ob der angegebene String nicht str1 und str2 enthält: ^(?!.*(str1|str2)).*$

3 Stimmen

Ja, oder Sie können den Lazy-Quantifikator verwenden: ^(?!.*?(?:str1|str2)).*$, abhängig von Ihren Daten. Hinzugefügt das ?:, da wir es nicht erfassen müssen.

1 Stimmen

Dies ist bei weitem die beste Antwort um den Faktor von 10xms. Wenn Sie Ihren jsfiddle-Code und die Ergebnisse zur Antwort hinzugefügt haben, könnten es die Leute bemerken. Ich frage mich, warum die faule Version schneller ist als die gierige Version, wenn es keinen hede gibt. Sollten sie nicht die gleiche Menge an Zeit in Anspruch nehmen?

68voto

akim Punkte 7572

Übrigens, da reguläre Sprachen (auch rationale Sprachen genannt) unter Komplementierung abgeschlossen sind, ist es immer möglich, einen regulären Ausdruck (auch rationalem Ausdruck genannt) zu finden, der einen anderen Ausdruck negiert. Aber nicht viele Tools implementieren dies.

Vcsn unterstützt diesen Operator (den es als {c}, postfix, bezeichnet).

Zuerst definieren Sie den Typ Ihrer Ausdrücke: Labels sind Buchstaben (lal_char), die beispielsweise aus a bis z auswählen (Die Definition des Alphabets bei der Arbeit mit Komplementierung ist natürlich sehr wichtig) und der "Wert", der für jedes Wort berechnet wird, ist einfach ein Boolescher Wert: 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

konvertiere diesen Ausdruck in einen Automaten:

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

Der entsprechende Automat

schließlich konvertiere 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[^]))))[^]*

wo + normalerweise als | dargestellt wird, \e das leere Wort bezeichnet und [^] normalerweise als . (beliebiges Zeichen) geschrieben wird. Also, mit ein wenig Umformung ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

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

10 Stimmen

Wahr, aber hässlich und nur für kleine Zeichensätze machbar. Das möchten Sie nicht mit Unicode-Zeichenfolgen tun :-)

1 Stimmen

Es gibt mehr Werkzeuge, die es ermöglichen, eines der beeindruckendsten ist Ragel. Dort würde es als (any* - ('hehe' any*)) für match mit Startausrichtung oder (any* -- ('hehe' any*)) für nicht ausgerichtete geschrieben werden.

2 Stimmen

@reinierpost: Warum findest du es hässlich und was ist das Problem mit Unicode? Ich kann beiden nicht zustimmen. (Ich habe keine Erfahrung mit vcsn, aber mit DFA).

66voto

Josh Lee Punkte 159535

Hier ist eine gute Erklärung, warum es nicht einfach ist, ein beliebiges Regex zu negieren. Ich muss den anderen Antworten jedoch zustimmen: Wenn dies mehr als nur eine hypothetische Frage ist, dann ist ein Regex hier nicht die richtige Wahl.

12 Stimmen

Einige Tools, insbesondere mysqldumpslow, bieten nur diese Möglichkeit, Daten zu filtern. In einem solchen Fall ist es die beste Lösung, ein Regex zu finden, um dies zu tun, anstatt das Tool neu zu schreiben (verschiedene Patches dafür wurden nicht von MySQL AB / Sun / Oracle aufgenommen.

1 Stimmen

Genau analog zu meiner Situation. Der Velocity-Template-Engine verwendet reguläre Ausdrücke, um zu entscheiden, wann eine Transformation angewendet wird (HTML escapen), und ich möchte, dass dies immer funktioniert, AUßER in einer Situation.

1 Stimmen

Welche Alternative gibt es hier? Ich bin bisher noch nie auf etwas gestoßen, das außer Regex eine genaue Zeichenfolgenübereinstimmung herstellen könnte. Wenn der/die OP eine Programmiersprache verwendet, stehen möglicherweise andere Tools zur Verfügung, aber wenn er/sie keinen Code schreibt, gibt es wahrscheinlich keine andere Wahl.

53voto

Falco Punkte 3127

Benchmarks

Ich habe beschlossen, einige der präsentierten Optionen zu bewerten und ihre Leistung zu vergleichen, sowie einige neue Funktionen zu verwenden. Benchmarking auf .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.

Results:

Ergebnisse sind Iterationen pro Sekunde als Median von 3 Durchläufen - Größere Zahl = Besser

01: ^((?!Regex Hero).)*$                    3.914   // Akzeptierte Antwort
02: ^(?:(?!Regex Hero).)*$                  5.034   // Mit Nicht-erfassender Gruppe
03: ^(?!.*?Regex Hero).*                   7.356   // Blick nach vorne am Anfang, wenn nicht gefunden, alles übereinstimmen
04: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Blick nach vorne nur auf dem rechten ersten Buchstaben
05: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Das Wort übereinstimmen und überprüfen, ob Sie immer noch am Zeilenanfang sind
06: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logikzweig: Finden Sie Regex Hero? nichts stimmt überein, ansonsten alles

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logikzweig in Perl - Schnelles FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direktes COMMIT & FAIL in Perl

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

Zusammenfassung:

Die insgesamt lesbarste und leistungsmäßig schnellste Lösung scheint Lösung 03 mit einem einfachen negativen Ausblick zu sein. Dies ist auch die schnellste Lösung für JavaScript, da JS die fortgeschritteneren Regex-Funktionen für die anderen Lösungen nicht unterstützt.

6 Stimmen

Sie sollten auch ^(?!.*hede) timen. /// Außerdem ist es wahrscheinlich besser, die Ausdrücke für das übereinstimmende Korpus und das nicht übereinstimmende Korpus getrennt zu bewerten, da es in der Regel so ist, dass die meisten Zeilen übereinstimmen oder die meisten Zeilen nicht übereinstimmen.

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