4 Stimmen

Regex.match in C# Leistungsproblem

Hallo zusammen ich verwende Regex.match in C#, um eine Textdatei Zeile für Zeile zu durchlaufen. Ich finde es wird mehr Zeit (ca. 2-4 Sekunden) verbringen, wenn die Zeile nicht mit dem Muster übereinstimmen. Aber verbringen weniger Zeit (weniger als 1 Sekunde), wenn übereinstimmen. Wer kann mir sagen, wie ich die Leistung verbessern kann?

Dies ist die Regex, die ich verwende:

^.*?\t.*?\t(?<npk>\d+)\t(?<bol>\w+)\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t\s*(?<netValue>[\d\.,]+)\t.*?\t.*?\t(?<item>\d{6})\t(?<salesDoc>\d+)\t(?<acGiDate>[\d\.]{10})\t.*?\t.*?\t.*?\t.*?\t.*?\t(?<delivery>\d+)\t\s*(?<billQuantity>\d+)\t.*?\t(?<material>[\w\-]+)\tIV$

8voto

Tim Pietzcker Punkte 311448

Leistungsprobleme, die nur dann auftreten, wenn ein Regex nicht übereinstimmt, sind häufig auf folgende Ursachen zurückzuführen katastrophales Backtracking . Dies ist der Fall, wenn eine Regex viele mögliche Kombinationen zulässt, die alle von der Regex-Engine ausprobiert werden müssen, bis sie als fehlgeschlagen erklärt werden kann.

In Ihrem Fall ist der Grund für das Scheitern offensichtlich:

Erstens sollte das, was Sie tun, nicht wirklich mit einem Regex, sondern eher mit einem CSV-Parser (oder TSV-Parser in Ihrem Fall) durchgeführt werden.

Wenn Sie jedoch mit Regex feststecken, müssen Sie noch etwas ändern. Ihr Problem ist, dass das Begrenzungszeichen \t kann auch mit dem Punkt ( . ), d. h. wenn nicht die gesamte Zeichenfolge übereinstimmt, muss die Regex-Engine Unmengen von Permutationen ausprobieren, wie ich oben beschrieben habe.

Ein großer Schritt nach vorn wäre daher die Änderung aller .*? in [^\t]* und ggf. zur Verdichtung von Wiederholungen unter Verwendung der {m,n} Betreiber:

^(?:[^\t]*\t){2}(?<npk>\d+)\t(?<bol>\w+)(?:\t[^\t]*){9}\t\s*(?<netValue>[\d\.,]+)(?:\t[^\t]*){2}\t(?<item>\d{6})\t(?<salesDoc>\d+)\t(?<acGiDate>[\d\.]{10})(?:\t[^\t]*){5}\t(?<delivery>\d+)\t\s*(?<billQuantity>\d+)\t[^\t]*\t(?<material>[\w\-]+)\tIV$

Ich hoffe, ich habe mich nicht verzählt :)


Nur zur Veranschaulichung:

Passend zu diesem Text

1   2   3   4   5   6   7   8   9   0

mit diesem Auszug aus Ihrer obigen Regex

.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t.*?\t\s*(?<netValue>[\d\.,]+)

benötigt die Regex-Engine 39 Schritte.

Wenn Sie ihn mit diesem Text füttern:

1   2   3   4   5   6   7   8   9   X

es braucht die Regex-Engine 4602 Schritte, um herauszufinden, dass es nicht übereinstimmen kann.

Wenn Sie

(?:[^\t]*\t){9}\s*(?<netValue>[\d\.,]+)

Stattdessen benötigt die Maschine 30 Schritte für eine erfolgreiche Übereinstimmung und nur 39 für einen Fehlversuch.

4voto

Marc Gravell Punkte 970173

Die Vorkompilierung hilft in der Regel:

private static readonly Regex re = new Regex(pattern, RegexOptions.Compiled);

Ich frage mich jedoch, ob es in diesem speziellen Fall an die Regex gebunden ist - vielleicht eine teure Rückreferenz. Regex ist nicht immer das zu verwendende Werkzeug, zum Beispiel...


Bearbeiten Sie jetzt, da es sich um abgegrenzte Daten zu handeln scheint:

Anstelle von Regex kann bei begrenzten Daten ein Parsing-Ansatz viel effektiver eingesetzt werden. Sie könnte sogar mit nur var parts=line.Split('\t') (und Zugang parts nach Index), aber wenn das scheitert - diese csv-Leser hat Optionen zur Steuerung der Trennzeichen usw.

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