100% CPU-Auslastung mit einem Regexp abhängig von der Eingangslänge

8

Ich versuche in Python eine Regexp zu finden, die mit einem beliebigen Zeichen übereinstimmen muss, aber drei oder mehr aufeinander folgende Kommas oder Semikola vermeidet. Mit anderen Worten, nur bis zu zwei aufeinanderfolgende Kommas oder Semikolons sind erlaubt.

So habe ich das zur Zeit:

%Vor%

Und es scheint zu funktionieren wie erwartet:

%Vor%

Aber wie ich anfange, die Länge des Eingabetextes zu erhöhen, scheint die Regexp viel mehr Zeit zu benötigen, um eine Antwort zu geben.

%Vor%

Und schließlich bleibt es in diesem Stadium völlig stecken und die CPU-Auslastung steigt auf 100%.

Ich bin mir nicht sicher, ob die Regexp optimiert werden kann oder etwas anderes involviert ist, jede Hilfe wird geschätzt.

    
julen 03.06.2011, 17:23
quelle

4 Antworten

20

Sie stoßen auf katastrophales Backtracking .

Der Grund dafür ist, dass Sie die Trennzeichen optional gemacht haben und daher der [^,;]+ -Teil (der sich selbst in einer Wiederholungsgruppe befindet) Ihrer Regex viele Permutationen (von baaaaaaaz ) ausprobieren wird, bevor Sie es schließlich tun müssen gestehen Versagen zu, wenn man mit mehr als zwei Kommas konfrontiert wird.

RegexBuddy bricht den Match-Versuch nach 1.000.000 Schritten der Regex-Engine mit Ihrer letzten Testreihe ab. Python wird es weiter versuchen.

Stellen Sie sich die Zeichenfolge baaz,,, vor:

Wenn Sie Ihre Regex testen, muss die Regex-Engine alle diese Punkte überprüfen:

  1. baaz,,<failure>
  2. baa + z,,<failure>
  3. ba + az,,<failure>
  4. ba + a + z,,<failure>
  5. b + aaz,,<failure>
  6. b + aa + z,,<failure>
  7. b + a + az,,<failure>
  8. b + a + a + z,,<failure>

vor dem Erklären des Gesamtfehlers. Sehen Sie, wie das exponentiell mit jedem zusätzlichen Zeichen wächst?

Ein Verhalten wie dieses kann mit Possessivquantifizern oder Atomgruppen vermieden werden, die beide von Pythons aktueller Regex-Engine leider nicht unterstützt werden. Aber Sie können leicht eine umgekehrte Überprüfung durchführen:

%Vor%

ohne eine Regex zu benötigen. Wenn ,;, und die Likes ebenfalls auftreten und ausgeschlossen werden sollen, verwenden Sie die Lösung von Andrew.

    
Tim Pietzcker 03.06.2011, 17:31
quelle
11

Ich denke, das Folgende sollte tun, was Sie wollen:

%Vor%

Dies schlägt fehl, wenn die Zeichenfolge drei oder mehr , oder ; in einer Zeile enthält. Wenn Sie wirklich wollen, dass es einem Zeichen entspricht, fügen Sie am Ende ein . hinzu.

Dies verwendet negatives Lookahead , was dazu führt, dass die gesamte Übereinstimmung fehlschlägt, wenn die Regex .*[,;]{3} übereinstimmen würde .

    
Andrew Clark 03.06.2011 17:34
quelle
4

Versuchen Sie diesen regulären Ausdruck:

%Vor%

Es passt wiederholt:

  • ein einzelnes Zeichen, das weder , noch ; oder
  • ist
  • a , , auf die entweder kein weiteres , oder ein ,, folgt, gefolgt von einem weiteren , oder
  • a ; , auf die entweder kein weiteres ; oder ein ;; folgt, auf das kein weiteres ; folgt

bis das Ende erreicht ist. Es ist sehr effizient, da es früh versagt, ohne viel Rückverfolgung zu machen.

    
Gumbo 03.06.2011 17:27
quelle
1

Wie wäre es mit dieser Idee, die mit dem Muster übereinstimmt, das Sie nicht wollen? %Code% In Python behalte nur diejenigen, die nicht übereinstimmen. Sollte schnell sein

    
millebii 03.06.2011 17:42
quelle

Tags und Links