Das mag nach einer dummen Frage klingen, aber ich hatte ein langes Gespräch mit einigen meiner Entwicklerkollegen und es klang nach einer lustigen Sache.
So; Was denkst du - wie sieht ein Regex aus, der niemals von einer Saite übertroffen wird?
Edit : Warum will ich das? Nun, erstens, weil ich es interessant finde, an einen solchen Ausdruck zu denken, und zweitens, weil ich ihn für ein Skript brauche.
In diesem Skript definiere ich ein Wörterbuch als Dictionary<string, Regex>
. Dies enthält, wie Sie sehen, eine Zeichenfolge und einen Ausdruck.
Basierend auf diesem Wörterbuch erstelle ich Methoden, die alle dieses Wörterbuch nur als Referenz dafür verwenden, wie sie ihre Arbeit erledigen sollen. Eine davon vergleicht die regulären Ausdrücke mit einer analysierten Protokolldatei.
Wenn ein Ausdruck übereinstimmt, Dictionary<string, long>
wird einem anderen ein Wert hinzugefügt, der vom Ausdruck zurückgegeben wird. Um alle Protokollnachrichten abzufangen, die nicht mit einem Ausdruck im Wörterbuch übereinstimmen, habe ich eine neue Gruppe mit dem Namen "unbekannt" erstellt.
Zu dieser Gruppe wird alles hinzugefügt, was mit nichts anderem übereinstimmt. Aber um zu verhindern, dass der "unbekannte" Ausdruck (aus Versehen) einer Protokollnachricht nicht entspricht, musste ich einen Ausdruck erstellen, der mit Sicherheit nie übereinstimmt, egal welche Zeichenfolge ich ihm gebe.
Da haben Sie also meinen Grund für diese "keine wirkliche Frage" ...
quelle
Antworten:
Dies ist eigentlich recht einfach,
obwohl es von der Implementierung / Flags*abhängt:Entspricht einem Zeichen
a
nach dem Ende der Zeichenfolge. Viel Glück.WARNUNG:
Dieser Ausdruck ist teuer - er scannt die gesamte Zeile, findet den Zeilenende-Anker und findet erst dann den nicht
a
und gibt eine negative Übereinstimmung zurück. (Siehe Kommentar unten für weitere Einzelheiten.)* Ursprünglich habe ich nicht viel über Regexp im Multiline-Modus nachgedacht, bei dem
$
auch das Ende einer Zeile übereinstimmt. Tatsächlich würde es mit der leeren Zeichenfolge direkt vor der neuen Zeile übereinstimmen , sodass ein gewöhnliches Zeichen wiea
niemals danach erscheinen kann$
.quelle
$a
. Das Perl-Äquivalent$(?:a)
ist auch sehr langsamperl -Mre=debug -e'$_=a x 50; /$(?:a)/'
.timeit
undpython3
.$a
dem Literaltext$a
, da er$
als Anker in diesem Muster ungültig ist.Hebelwirkung
negative lookahead
:Diese RE ist ein Widerspruch und wird daher niemals mit irgendetwas übereinstimmen.
HINWEIS:
In Python fügt re.match () implizit
\A
am Anfang des regulären Ausdrucks einen Zeichenfolgenanfang ( ) an. Dieser Anker ist wichtig für die Leistung: Ohne ihn wird die gesamte Zeichenfolge gescannt. Diejenigen, die Python nicht verwenden, möchten den Anker explizit hinzufügen:quelle
(?=x)(?!x)
und so weiter (Verkettungen von widersprüchlichen Lookaheads und dasselbe für Lookbehinds), und viele davon funktionieren auch für beliebige Werte vonx
(Lookbehinds benötigenx
s, die mit Zeichenfolgen fester Länge übereinstimmen).r'a\bc'
, die für alle interessierenden RE-Engines funktioniert): Suchen Sie nach einer Wortgrenze, die auf beiden Seiten unmittelbar von Buchstaben umgeben ist (Variante: Nichtwortzeichen auf beide Seiten).perl -Mre=debug -e'$_=x x 8; /(?!x)x/'
. Sie können es schneller machen, indem Sie es am Anfang\A(?!x)x
oder am Ende verankern(?!x)x\z
.perl -Mre=debug -e'$_=x x 8; /(?!x)x\z/; /\A(?!x)x/'
Eine, die vermisst wurde:
Es kann nicht übereinstimmen, da die leere Zeichenfolge keine Wortgrenze enthält. In Python 2.5 getestet.
quelle
\`\b\'
funktioniert jedoch , indem "Anfang / Ende des Textes" durch die Emacs-Syntax ersetzt wird (im Gegensatz zu "Anfang / Ende" der Linie ").umschauen:
(?=a)b
Für Regex-Neulinge: Der positive Blick nach vorne
(?=a)
stellt sicher, dass das nächste Zeichen ista
, ändert jedoch nicht den Suchort (oder fügt das 'a' in die übereinstimmende Zeichenfolge ein). Nachdem das nächste Zeichen bestätigt wurde, stimmta
der verbleibende Teil von regex (b
) nur dann überein, wenn das nächste Zeichen istb
. Daher stimmt diese Regex nur überein, wenn ein Zeichen beidea
undb
gleichzeitig ist.quelle
a\bc
, wobei\b
es sich um einen Ausdruck mit der Breite Null handelt, der der Wortgrenze entspricht.Es kann nicht in der Mitte eines Wortes erscheinen, zu dem wir es zwingen.
quelle
a
im Text sucht und diese testet .$.
.^
$.^
(?!)
quelle
^
nur als erstes Zeichen eines$
regulären Ausdrucks eine besondere Bedeutung hat und nur am Ende eines regulären Ausdrucks eine besondere Bedeutung hat, es sei denn, der reguläre Ausdruck ist ein mehrzeiliger Ausdruck./$./
bedeutet in Perl etwas ganz anderes. Dies bedeutet, dass der aktuelle Wert von$.
(Eingabezeilennummer) übereinstimmt . Sogar/$(.)/
könnte etwas passen, wenn Sieuse re '/s';
vorher geschrieben haben. (perl -E'say "\n" =~ /$(.)/s || 0'
)^
und$
sind nur am Anfang bzw. Ende des Musters speziell, so dass keines von$.
oder.^
oder$.^
funktionieren würde.(?!)
ist eine Perl / PCRE-Funktion, glaube ich.Maximale Übereinstimmung
Mindestens eine,
a
gefolgt von einer beliebigen Anzahla
, ohne Rückverfolgung. Versuchen Sie dann, eine weitere zu findena
.oder Unabhängiger Unterausdruck
Dies entspricht dem Einfügen
a+
eines unabhängigen Unterausdrucks, gefolgt von einem anderena
.quelle
Perl 5.10 unterstützt spezielle Steuerwörter namens "Verben", die der
(*...)
Reihe nach eingeschlossen sind. (Vergleiche mit einer(?...)
speziellen Reihenfolge.) Darunter befindet sich auch das(*FAIL)
Verb das sofort vom regulären Ausdruck zurückkehrt.Beachten Sie, dass Verben kurz darauf auch in PCRE implementiert werden, sodass Sie sie auch in PHP oder anderen Sprachen mithilfe der PCRE-Bibliothek verwenden können. (Sie können jedoch nicht in Python oder Ruby. Sie verwenden ihre eigene Engine.)
quelle
\b
stimmt mit Wortgrenzen überein - die Position zwischen einem Buchstaben und einem Nichtbuchstaben (oder der Zeichenfolgengrenze).\B
ist seine Ergänzung - es entspricht der Position zwischen zwei Buchstaben oder zwischen Nichtbuchstaben.Zusammen können sie keiner Position entsprechen.
Siehe auch:
quelle
^\B\b
. In Sprachen, in denen "Textanfang" und "Zeilenanfang" unterschiedliche Syntax haben, möchten Sie die Syntax "Textanfang" verwenden, andernfalls testen Sie jede Zeile. (zB in Emacs wäre dies\`\B\b
oder"\\`\\B\\b"
.)^
ist dies bei bestimmten regulären Ausdruckssyntaxen (z. B. POSIX BRE) problematisch, bei denen^
es sich nur um einen Anker handelt, wenn es sich um das erste Zeichen handelt des Musters und entspricht ansonsten einem wörtlichen^
Zeichen.:)
- dies ist eine unpraktische Frage, bei der das Ziel darin bestand, eine interessante Antwort zu finden - keine effiziente Antwort. Das Muster kann jedoch in der Liner-Zeit (mit der Größe der Zielzeichenfolge) verworfen werden, sodass es für einen regulären Ausdruck nicht schlecht ist. Die meisten Muster hier sind gleich und können sogar^
linear sein, wenn es nicht optimiert ist.Das scheint zu funktionieren:
quelle
$.
. In diesem Fall müssen Sie auf$(.)
oder gleichwertiger zurückgreifen$(?:.)
.$.
einem Literal,$
gefolgt von einem beliebigen Zeichen, da$
es als Anker in diesem Muster ungültig ist.Wie wäre es
$^
oder vielleicht(?!)
?quelle
^
der Anfang und$
das Ende einer Zeile übereinstimmen .(?!)
- einen negativen Lookahead für eine leere Zeichenfolge. Einige Regex-Varianten behandeln dies jedoch auch als Syntaxfehler.$^
werden diese Literalzeichen übereinstimmen, da die Zeichen als Anker ungültig sind (dh der Grund, warum Sie das Muster verwendet haben, führt dazu, dass es nicht das tut, was Sie wollten.)Das schnellste wird sein:
'a' kann ein beliebiges nicht spezielles Zeichen sein ('x', 'y'). Die Implementierung von Knio ist vielleicht etwas reiner, aber diese ist schneller für alle Zeichenfolgen, die nicht mit dem von Ihnen ausgewählten Zeichen anstelle von 'a' beginnen, da sie in diesen Fällen nicht nach dem ersten Zeichen, sondern nach dem zweiten übereinstimmen.
quelle
^
ist nur als erstes Zeichen und ähnlich mit$
. Mit jedem Unix-Tool stimmt dieser reguläre Ausdruck mit allem überein, der die Literalzeichenfolge enthälta^
.>^
.Python wird es nicht akzeptieren, aber Perl wird:
Diese Regex sollte (theoretisch) versuchen, eine unendliche (gerade) Anzahl von
w
s zu finden, da die erste Gruppe (die()
s) in sich selbst rekursiv ist. Perl scheint keine Warnungen auszugeben, auch nicht unteruse strict; use warnings;
, daher gehe ich davon aus, dass es zumindest gültig ist und meine (minimalen) Tests mit nichts übereinstimmen, also reiche ich es für Ihre Kritik ein.quelle
perl -Mre=debug -e'"www wwww wwwww wwwwww" =~ /(w\1w)/'
[^\d\D]
oder(?=a)b
odera$a
odera^a
quelle
Dies funktioniert nicht für Python und viele andere Sprachen, ist jedoch in einem Javascript-Regex
[]
eine gültige Zeichenklasse, die nicht zugeordnet werden kann. Folgendes sollte also sofort fehlschlagen, unabhängig von der Eingabe:Ich mag es besser als
/$a/
weil es mir klar seine Absicht kommuniziert. Und wann immer Sie es jemals brauchen würden, ich brauchte es, weil ich einen Fallback für ein dynamisch kompiliertes Muster brauchte, das auf Benutzereingaben basiert. Wenn das Muster ungültig ist, muss ich es durch ein Muster ersetzen, das mit nichts übereinstimmt. Vereinfacht sieht es so aus:quelle
Alle Beispiele mit einem Boundary Matcher folgen demselben Rezept. Rezept:
Nehmen Sie einen der Boundary Matcher: ^, $, \ b, \ A, \ Z, \ z
Machen Sie das Gegenteil von dem, wofür sie bestimmt sind
Beispiele:
^ und \ A sind für den Anfang gedacht, verwenden Sie sie also nicht am Anfang
\ b stimmt mit einer Wortgrenze überein, verwenden Sie sie also dazwischen
$, \ Z und \ z sind für das Ende gedacht, verwenden Sie sie also am Ende nicht
Andere beinhalten die Verwendung von Lookahead und Lookbehind, die ebenfalls mit der gleichen Analogie funktionieren: Wenn Sie einen positiven oder negativen Lookahead geben, gefolgt von etwas Gegenteiligem
Wenn Sie positiv oder negativ aussehen, folgen Sie etwas Gegenteiligem
Es könnte mehr solche Muster und mehr solche Analogien geben.
quelle
So viele gute Antworten!
Ähnlich wie bei @ nivks Antwort möchte ich den Leistungsvergleich für Perl für verschiedene Varianten von nie übereinstimmendem regulären Ausdruck teilen.
Regex-Geschwindigkeit:
Regex-Geschwindigkeit:
(Ubuntu unter Intel i5-3320M, Linux-Kernel 4.13, Perl 5.26)
quelle
Ich glaube das
deckt sogar Fälle ab, in denen der reguläre Ausdruck Flags wie MULTILINE, DOTALL usw. enthält.
Ich glaube (aber ich habe es nicht bewertet), dass unabhängig von der Länge (> 0) der Zeichenfolge zwischen
\Z
und\A
die Zeit bis zum Ausfall konstant sein sollte.quelle
oder
Mit PCRE und PERL können Sie dieses Backtracking-Steuerverb verwenden, das das Muster zum sofortigen Fehlschlagen zwingt.
quelle
Nachdem ich einige dieser großartigen Antworten gesehen hatte, veranlasste mich der Kommentar von @ arantius (in Bezug auf Timing
$x
vsx^
vs(?!x)x
) zu der aktuell akzeptierten Antwort, einige der bisher gegebenen Lösungen zeitlich festzulegen .Unter Verwendung des 275k-Zeilenstandards von @ arantius habe ich die folgenden Tests in Python (v3.5.2, IPython 6.2.1) ausgeführt.
TL; DR:
'x^'
und'x\by'
sind mit einem Faktor von mindestens ~ 16 die schnellsten und gehörten entgegen @ arantius 'Feststellung(?!x)x
zu den langsamsten (~ 37-mal langsamer). Die Geschwindigkeitsfrage ist also sicherlich implementierungsabhängig. Testen Sie es selbst auf Ihrem vorgesehenen System, bevor Sie festlegen, ob Geschwindigkeit für Sie wichtig ist.UPDATE: Es gibt anscheinend eine große Diskrepanz zwischen Timing
'x^'
und'a^'
. Weitere Informationen finden Sie in dieser Frage und in der vorherigen Bearbeitung für die langsameren Timings mita
anstelle vonx
.Als ich dies zum ersten Mal ausführte, vergaß ich,
r
die letzten drei Ausdrücke zu vergessen, und wurde daher als Rücktaste'\b'
interpretiert'\x08'
. Zu meiner Überraschung war es'a\x08c'
jedoch schneller als das bisher schnellste Ergebnis! Um fair zu sein, es wird immer noch zu diesem Text passen, aber ich dachte, es wäre immer noch erwähnenswert, weil ich nicht sicher bin, warum es schneller ist.Meine Testdatei wurde mit einer Formel für "... lesbare Inhalte und keine doppelten Zeilen" (unter Ubuntu 16.04) erstellt:
quelle
\B\b
ist in Bezug auf die Leistung schrecklich fehlerhaft (wie jedes Muster, das nicht an einer Position verankert ist, aber dieses Muster ist besonders schlecht). Versuchen Sie^\B\b
stattdessen Benchmarking .Leere Regex
Der beste reguläre Ausdruck, der niemals mit etwas übereinstimmt, ist ein leerer regulärer Ausdruck. Aber ich bin nicht sicher, ob alle Regex-Engines das akzeptieren werden.
Unmöglicher Regex
Die andere Lösung besteht darin, einen unmöglichen regulären Ausdruck zu erstellen. Ich habe festgestellt, dass
$-^
die Berechnung unabhängig von der Größe Ihres Textes nur zwei Schritte dauert ( https://regex101.com/r/yjcs1Z/1 ).Als Referenz:
$^
und$.
mache 36 Schritte, um zu berechnen -> O (1)\b\B
macht 1507 Schritte in meinem Sample und erhöht sich mit der Anzahl der Zeichen in Ihrer Zeichenfolge -> O (n)Populärer Thread zu dieser Frage:
quelle
Vielleicht das?
quelle
re.compile('$.+^', re.MULTILINE|re.DOTALL).search('a\nb\nc\n')
Gibt ein Übereinstimmungsobjekt zurück, das dem b und c (und allen benachbarten und dazwischen liegenden Zeilenumbrüchen) entspricht. Der von mir empfohlene Negativ-Lookahead-Ansatz funktioniert (dh passt nicht zu irgendetwas) für jede Kombination von Flags, mit denen er kompiliert werden könnte.$
und^
./\z.+\A/
(siehe perldoc perlre ). Dies verhindert, dass der Mehrzeilen- und Einzeilenmodus (use re '/ms'
) dies beeinflusst.und ... durch alle druckbaren Symbole ersetzen;). Das ist für eine Textdatei.
quelle
[^\x00-\xFF]+
(für bytebasierte Implementierungen), um jedes mögliche Zeichen abzufangen.[^\s\S]
. Aber wie Ferdinand Beyer bereits sagte, würde es zu einer leeren Zeichenfolge passen.*
; Lassen Sie das weg oder ersetzen Sie es durch+
, und es muss mindestens einem Zeichen entsprechen. Wenn die Klasse alle möglichen Zeichen ausschließt, kann sie mit nichts übereinstimmen.Was ist mit anstelle von Regex, verwenden Sie einfach eine immer falsche if-Anweisung? In Javascript:
quelle
Eine tragbare Lösung, die nicht von der Regexp-Implementierung abhängt, besteht darin, nur eine konstante Zeichenfolge zu verwenden, von der Sie sicher sind, dass sie niemals in den Protokollnachrichten angezeigt wird. Erstellen Sie beispielsweise eine Zeichenfolge, die auf Folgendem basiert:
Sicher, dies ist keine intellektuelle Herausforderung, sondern eher eine Klebebandprogrammierung .
quelle
Erstellt ein Muster, das nur alphanumerische Zeichen und '
-
' enthält (keines davon sind Regex-Sonderzeichen), aber es ist statistisch unmöglich, dass dieselbe Zeichenfolge irgendwo zuvor angezeigt wurde (da dies der springende Punkt einer GUID ist.)quelle