Ist Regex zu langsam? Beispiele aus dem wirklichen Leben, in denen einfache Nicht-Regex-Alternativen besser sind
Ich habe Menschen gesehen, die hier Kommentare wie "regex ist zu langsam!", oder "warum würden Sie etwas tun, so einfach mit regex!" (und dann einen 10+ Zeilen alternative statt), etc.
Ich habe nicht wirklich verwendet werden, regex im industriellen Umfeld, so bin ich neugierig, ob es Anwendungen, bei denen regex ist demonstratably einfach zu langsam, UNDwo ein einfach nicht-regex-alternative existiert, das führt deutlich (vielleicht sogar asymptotisch!) besser.
Offensichtlich viele hoch spezialisierte string-Manipulationen mit raffinierten string-algorithmen übertreffen regex leicht, aber ich spreche von Fällen, in denen eine einfache Lösung existiert und deutlich übertrifft regex.
Was zählt, so einfach ist subjektiv, natürlich, aber ich denke, dass ein angemessener standard ist, dass, wenn es verwendet nur String
StringBuilder
usw, dann ist es wohl einfach.
Hinweis: ich würde sehr mich über Antworten freuen, veranschaulichen die folgenden:
- ein Anfänger-level-regex-Lösung auf einem nicht-Spielzeug real-life-problem führt, dass die schrecklich
- das einfache, nicht-regex-Lösung
- die expert-level-regex rewrite führt, dass vergleichsweise
InformationsquelleAutor der Frage polygenelubricants | 2010-04-19
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich erinnere mich an ein lehrbuch-Beispiel. Beachten Sie, dass keine der folgenden Vorgehensweisen empfohlen, für die Produktion verwenden! Verwenden Sie eine korrekte CSV-parsing statt.
Den Fehler gemacht in diesem Beispiel ist durchaus üblich: der Punkt, wo ein schmaler Charakter-Klasse besser geeignet ist.
In eine CSV-Datei enthält in jeder Zeile genau 12 Ganzzahlen, getrennt durch Kommas, finden Sie die Linien mit 13 in der 6. Stelle (egal wo sonst eine 13 sein kann).
Verwenden wir eine regex, die genau 11 Kommas:
Diese Weise jeder ".*" ist beschränkt auf eine einzelne Zahl. Dieser regex löst die Aufgabe, hat aber sehr schlechte Leistung. (Etwa 600 Mikrosekunden pro string auf meinem computer, mit wenig Unterschied zwischen den passenden und den unvergleichlichen Zeichenfolgen).
Eine einfache, nicht-regex-Lösung wäre
split()
jeder Zeile und vergleichen Sie das 6. element. (Viel schneller: 9 Mikrosekunden pro string.)Grund die regex so langsam ist, dass der " * " - Quantifizierer gierig standardmäßig, und so der erste ".*" versucht, die ganze Reihe, und danach beginnt das ansetzen von Zeichen. Die Laufzeit ist exponentiell in der Anzahl der zahlen in einer Zeile.
So ersetzen wir die gierigen Quantifizierer mit der nur ungern ein:
Dieser führt die Art und Weise besser für ein passendes string (durch einen Faktor von 100), hat aber nahezu unverändert, die Leistung für einen nicht-gematchte string.
Eine performante regex ersetzt den Punkt durch die Charakter-Klasse "[^,]":
(Dies muss von 3,7 Mikrosekunden pro Zeile für die matched-string und 2.4 für die unübertroffene Saiten auf meinem computer.)
InformationsquelleAutor der Antwort Christian Semrau
Experimentierte ich ein wenig mit der Leistung der verschiedenen Konstrukte, und leider habe ich entdeckt, dass Java regex nicht, was ich als sehr machbar Optimierungen.
Java-regex nimmt
O(N)
passend"(?s)^.*+$"
Dies ist sehr enttäuschend. Es ist verständlich für
".*"
zu nehmenO(N)
aber mit der Optimierung "Hinweise" in form von Anker (^
und$
) und single-line-ModusPattern.DOTALL/(?s)
sogar die Wiederholung besitzergreifend (d.h. ohne backtracking), die regex-engine immer noch nicht sehen konnte, dass dieses Spiel jede saite, und immer noch Spiel inO(N)
.Dieses Muster ist nicht sehr nützlich, natürlich, aber überlege das nächste problem.
Java-regex nimmt
O(N)
passend"(?s)^A.*Z$"
Wieder, ich hatte gehofft, dass die regex-engine kann sehen, dass Dank der Anker-und single-line Modus, dies ist im wesentlichen der gleiche wie der
O(1)
nicht-regex:Leider, Nein, das ist noch
O(N)
. Sehr enttäuschend. Noch, nicht sehr überzeugend, weil eine schöne und einfache, nicht-regex-alternative vorhanden ist.Java-regex nimmt
O(N)
passend"(?s)^.*[aeiou]{3}$"
Diesem Muster entspricht, strings, enden mit 3 kleingeschriebene Vokale. Es ist keine schöne und einfache, nicht-regex-alternative, aber Sie können noch etwas schreiben, was nicht-regex entspricht dies in
O(1)
da Sie müssen nur überprüfen Sie die letzten 3 Zeichen (der Einfachheit halber können wir annehmen, dass die Länge der Zeichenfolge ist mindestens 3).Ich habe auch versucht
"(?s)^.*$(?<=[aeiou]{3})"
in einem Versuch zu sagen, die regex-engine einfach zu ignorieren alles andere, und überprüfen Sie die letzten 3 Zeichen, aber das ist natürlich nochO(N)
(das folgt aus dem ersten Abschnitt oben).In diesem speziellen Szenario, aber regex kann nützlich durch die Kombination mit
substring
. Das ist, statt zu sehen, wenn der ganze string dem Muster entspricht, können Sie manuell einschränken, das Muster zu versuchen, zu entsprechen, nur die letzten 3 Zeichensubstring
. Im Allgemeinen, wenn Sie wissen, bevor die hand, dass das Muster hat eine endliche Länge maximal passen, können Siesubstring
die notwendige Anzahl von Zeichen vom Ende einer sehr langen string und regex nur auf diesen Teil.Testumgebung
Die Länge der Zeichenfolge in diesem test wächst exponentiell. Wenn Sie diesen test ausführen, werden Sie feststellen, dass es beginnt langsam richtig zu sich nach
10
(d.h. string-Länge 1024). Wenn Sie die Auskommentierung dersubstring
Linie, jedoch der gesamte test wird komplett in kürzester Zeit (was auch bestätigt, dass das problem nicht weil ich nichtPattern.compile
das ergäbe eine Konstante Verbesserung im besten, aber eher, weil die patttern nimmtO(N)
passen, was problematisch ist, wenn die asymptotische Wachstum vonN
ist exponentiell).Fazit
Scheint es, dass Java-regex-tut wenig, um keine Optimierung auf der Grundlage der Muster. Suffix-matching im besonderen ist besonders aufwendig, denn die regex muss noch gehen Sie durch die gesamte Länge der Zeichenfolge.
Zum Glück tun die regex auf die gehackte suffix mit
substring
(wenn Sie wissen, dass die maximale Länge der übereinstimmung) kann immer noch erlauben, Sie zu verwenden, regex für suffix-matching in Zeit unabhängig von der Länge der Eingabe-Zeichenkette.//update: eigentlich habe ich nur realisiert, dass dies für Präfix-Abgleich zu. Java-regex-matches ein
O(1)
Länge Präfix-Muster inO(N)
. Das ist"(?s)^[aeiou]{3}.*$"
prüft, ob ein string beginnt mit 3 Kleinbuchstaben inO(N)
sein, wenn es optimierbare zuO(1)
.Dachte ich präfixvergleich wäre mehr regex-freundlich, aber ich glaube nicht, dass es möglich ist zu kommen mit einer
O(1)
-runtime-Muster, das auf die oben (es sei denn jemand kann beweisen, mich nicht falsch).Natürlich können Sie die
s.substring(0, 3).matches("(?s)^[aeiou]{3}.*$")
"trick", aber das Muster selbst ist nochO(N)
; Sie haben gerade manuell reduziertN
auf eine Konstante durch die Verwendungsubstring
.Also für jede Art von finite-length prefix - /suffix-matching für ein wirklich langer string, sollten Sie Vorverarbeiten mit
substring
vor der Verwendung von regex; sonst ist esO(N)
woO(1)
genügt.InformationsquelleAutor der Antwort polygenelubricants
Regex ist nicht wirken sehr langsam. basic pattern-matching ist O(n), hart, um zu verbessern, durchaus für nicht-triviale Muster.
InformationsquelleAutor der Antwort Henk Holterman
In meinen tests habe ich Folgendes gefunden:
In der java-String.split-Methode (mit regex) nahm 2176ms unter der 1.000.000 Iterationen.
Mit dieser benutzerdefinierten split-Methode nahm 43ms unter der 1.000.000 Iterationen.
Natürlich, es wird nur funktionieren, wenn Ihre "regex" ist ganz wörtlich, aber in jenen Fällen,
es wird viel schneller sein.
Also um deine Frage zu beantworten, ist es theoretisch schneller? Ja, absolut, mein Algorithmus ist O(n), wobei n die Länge des Strings zu splitten. (Ich bin mir nicht sicher, was regex wäre). Ist es praktisch schneller? Gut, über 1 Millionen Iterationen, Sparte ich im Grunde 2 Sekunden. Also, es hängt von Ihren Bedürfnissen, ich denke, aber ich würde nicht zu viel sorgen über das Zurückportieren alle code, regex verwendet, um nicht-regex-Versionen, und in der Tat, dass es notwendig werden könnte, sowieso, wenn das Muster ist sehr Komplex, eine wörtliche split wie diese nicht funktionieren. Allerdings, wenn Sie Spalten sich auf, sagen wir mal, Kommas, diese Methode führt viel besser, obwohl "viel besser" ist subjektiv hier.
InformationsquelleAutor der Antwort LadyCailin
Gut, nicht immer, aber manchmal langsam, hängt davon ab, patterns und Implementierungen.
Einen schnellen Beispiel, 2x langsamer als normal zu ersetzen, aber ich glaube nicht, dass Ihr das langsam.
InformationsquelleAutor der Antwort YOU