Performante reguläre Ausdrücke schreiben

Hier werden einige der wichtigen Aspekte erläutert, die Sie beim Erstellen von performanten regulären Ausdrücken berücksichtigen müssen.

Die regulären Ausdruckskonstrukte, die mit Parsern, Labels und Datenfiltern verwendet werden können, finden Sie in der Dokumentation zu Java Platform Standard Ed. 8.

Informationen zu Suchvorgängen und regulärem Ausdruck zur Abfragezeit finden Sie in der Syntax RE2J unter Java Implementation of RE2.

Zeichenklassen

Die Zeichenklassen geben die Zeichen an, die Sie abgleichen oder nicht abgleichen möchten. Stellen Sie sicher, dass Sie . in .*s durch ein spezifischeres Zeichen ersetzen. Der Teil .* wird stets an das Ende der Eingabe verschoben und dann auf einen zuvor gespeicherten Status zurückgesetzt (Backtracking), um die Suche nach einer Übereinstimmung fortzusetzen. Wenn Sie eine bestimmte Zeichenklasse verwenden, können Sie kontrollieren, wie viele Zeichen die reguläre Ausdrucks-Engine durch * konsumiert. Dadurch können Sie das stetige Backtracking stoppen.

Beispiel für einen regulären Ausdruck:

(\d{4})(\d{2})(\d{2}),(\S+),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([\S\s]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Eingabe:

20150220,201502,16798186260,tvN,Entertainment/Music,Female,67,2,Individual,ollehtv Economic,Commercial Housing,5587,0,2,0,1,1

Als Ergebnis des angegebenen regulären Ausdrucks kann der Abgleich in Backtracking versetzt werden. Diese Situation wird von Oracle Logging Analytics erkannt, und der Abgleichsvorgang wird abgebrochen.

Wenn Sie den regulären Ausdruck in das folgende Beispiel ändern, können Sie sicherstellen, dass der Abgleich schneller abgeschlossen wird. Beachten Sie, dass [\S\s]* in [^,] geändert wird, wodurch unnötiges Backtracking vermieden wird.

(\d{4})(\d{2})(\d{2}),(\S+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+),([0-9.]+)

Lazy-Quantifizierer

In vielen regulären Ausdrücken können Greedy-Quantifizierer (.*s) problemlos durch Lazy-Quantifizierer (.*?s) ersetzt werden. Dadurch verbessert sich die Performance des regulären Ausdrucks bei gleichem Ergebnis.

Beachten Sie die folgende Abfrage:

Trace file /u01/app/oracle/diag/rdbms/navisdb/NAVISDB/trace/NAVISDB_arc0_3941.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/app/oracle/product/11.2.0/db_1
System name: Linux
Node name: NAVISDB
Release: 2.6.18-308.el5
Version: #1 SMP Fri Jan 27 17:17:51 EST 2012
Machine: x86_64
Instance name: NAVISDB
Redo thread mounted by this instance: 1
Oracle process number: 21
Unix process pid: 3941, image: oracle@NAVISDB (ARC0)

Betrachten Sie den folgenden regulären Greedy-Ausdruck für die angegebene Eingabe:

Trace\sfile\s(\S*).*ORACLE_HOME\s*[:=]\s*(\S*).*System\sname:\s*(\S*).*Node\sname:\s*(\S*).*Release:\s*(\S*).*Machine:\s*(\S*).*Instance\sname:\s*(\S*).*Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*Oracle\sprocess\snumber:\s*(\d*).*Unix\sprocess\spid:\s(\d*).*image:\s+([^\n\r]*)

Die reguläre Ausdrucks-Engine wird bei jedem Vorkommen von .*. an das Ende der Eingabe versetzt. Wenn .* zum ersten Mal vorkommt, wird die gesamte Eingabe abgearbeitet und dann Backtracking zu ORACLE_HOME vorgenommen. Das ist eine ineffiziente Abgleichsmethode. Der alternative reguläre Lazy-Ausdruck:

Trace\sfileRelease:\s*(\S*).*?Machine:\s*(\S*).*?Instance\sname:\s*(\S*).*?Redo\sthread\smounted\sby\sthis\sinstance:\s(\d*).*?Oracle\sprocess\snumber:\s*(\d*).*?Unix\sprocess\spid:\s(\d*).*?image:\s+([^\n\r]*)

Mit dem oben genannten regulären Lazy-Ausdruck würde die Abarbeitung am Anfang der Zeichenfolge beginnen und bis Erreichung von ORACLE_HOME fortgesetzt werden. An dieser Stelle kann der Abgleich mit der restlichen Zeichenfolge fortgesetzt werden.

Hinweis: Wenn das Feld ORACLE_HOME zu Beginn der Eingabe vorkommt, muss der Lazy-Quantifizierer verwendet werden. Wenn das Feld ORACLE_HOME gegen Ende vorkommt, kann der Greedy-Quantifizierer geeignet sein.

Anker

Anker geben der regulären Ausdrucks-Engine an, dass der Cursor an einer bestimmten Stelle in der Eingabe platziert werden soll. Die häufigsten Anker sind ^ und $ für Anfang bzw. Ende der Eingabe.

Reguläre Ausdrücke zum Finden einer IPv4-Adresse:

\d{1,3}\.d{1,3}.\d{1,3}.\d{1,3}
^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

Der zweite reguläre Ausdruck beginnt mit ^ und gibt an, dass die IP-Adresse am Anfang der Eingabe angezeigt werden soll.

In einer Eingabe wie dem folgenden Beispiel wird nach dem regulären Ausdruck gesucht:

107.21.20.1 - - [07/Dec/2012:18:55:53 -0500] "GET
      /extension/bsupport/design/cl/images/btn_letschat.png HTTP/1.1" 200
    2144

Eine nicht übereinstimmende Eingabe würde dem folgenden Beispiel ähneln:

[07/Dec/2012:23:57:13 +0000] 1354924633 GET "/favicon.ico" "" HTTP/1.1 200 82726 "-"
      "ELB-HealthChecker/1.0"

Der zweite reguläre Ausdruck (der mit ^ beginnt) wird bei nicht übereinstimmender Eingabe schneller ausgeführt, da er die nicht übereinstimmende Eingabe umgehend verwirft.

Die Bedeutung der Alternativen

Bei Alternativen muss die Reihenfolge beachtet werden. Platzieren Sie daher die häufigeren Optionen vorne, damit sie schneller abgeglichen werden können. Wenn die selteneren Optionen zuerst genannt werden, verschwendet die Engine für reguläre Ausdrücke Zeit mit deren Prüfung, bevor sie die häufigeren Optionen mit höheren Erfolgsaussichten prüft. Versuchen Sie außerdem, häufige Muster zu extrahieren. Beispiel: Verwenden Sie ab(cd|ef) anstelle von (abcd|abef).

Betrachten Sie die folgenden regulären Ausdrücke:

{TIMEDATE}\s{0,1}:\s*(?:|\[)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*
{TIMEDATE}\s{0,1}:\s*(?:\[|)(\w+)(?:\:|\])(?:\[|)(\d+)(?:\:|\])(.{1,1000}).*

Eingabe:

2014-06-16 12:13:46.743: [UiServer][1166092608] {0:7:2} Done for
ctx=0x2aaab45d8330

Der zweite reguläre Ausdruck wird schneller abgeglichen, da die Alternative zuerst nach dem Zeichen [ gefolgt von Null sucht. Da die Eingabe [ enthält, wird der Abgleich schneller ausgeführt.

Beispielparseausdrücke

Anhand der folgenden Beispielparseausdrücke können Sie einen geeigneten Parseausdruck zum Extrahieren von Werten aus der Logdatei erstellen.

Eine Logdatei enthält Einträge, die durch Verkettung mehrerer Feldwerte generiert werden. Möglicherweise müssen Sie nicht alle Feldwerte für die Analyse einer Logdatei mit einem bestimmten Format anzeigen. Mit einem Parser können Sie die Werte nur aus den Feldern extrahieren, die Sie anzeigen möchten.

Ein Parser extrahiert Felder aus einer Logdatei basierend auf dem von Ihnen definierten Parseausdruck. Ein Parseausdruck wird in Form eines regulären Ausdrucks geschrieben, der ein Suchmuster definiert. In einem Parseausdruck schließen Sie Suchmuster in Klammern () für jedes übereinstimmende Feld ein, das Sie aus einem Logeintrag extrahieren möchten. Jeder Wert, der mit einem Suchmuster übereinstimmt, das sich außerhalb der Klammern befindet, wird nicht extrahiert.

Beispiel 1

Sie möchten die folgenden Beispiellogeinträge parsen:

Jun 20 15:19:29 hostabc rpc.gssd[2239]: ERROR: can't open clnt5aa9: No such file or directory
Jul 29 11:26:28 hostabc kernel: FS-Cache: Loaded
Jul 29 11:26:28 hostxyz kernel: FS-Cache: Netfs 'nfs' registered for caching

Verwenden Sie den folgenden Parseausdruck:

(\S+)\s+(\d+)\s(\d+):(\d+):(\d+)\s(\S+)\s(?:([^:\[]+)(?:\[(\d+)\])?:\s+)?(.+)

Im vorherigen Beispiel werden unter anderem die folgenden Werte vom Parseausdruck erfasst:

  • (\S+): Mehrere Nicht-Leerzeichen für den Monat

  • (\d+): Mehrere Nicht-Leerzeichen für den Tag

  • (?:([^:\[]+): (Optional) Alle Zeichen außer ^, :, \, []; das gilt für den Servicenamen

  • (.+): (Optional) Primärer Meldungsinhalt

Beispiel 2

Sie möchten die folgenden Beispiellogeinträge parsen:

####<Apr 27, 2014 4:01:42 AM PDT> <Info> <EJB> <host> <AdminServer> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <OracleSystemUser> <BEA1-13E2AD6CAC583057A4BD> <b3c34d62475d5b0b:6e1e6d7b:143df86ae85:-8000-000000000000cac6> <1398596502577> <BEA-010227> <EJB Exception occurred during invocation from home or business: weblogic.ejb.container.internal.StatelessEJBHomeImpl@2f9ea244 threw exception: javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec. The exception is:java.lang.StackOverflowError>
####<Jul 30, 2014 8:43:48 AM PDT> <Info> <RJVM> <example.com> <> <Thread-9> <> <> <> <1406735028770> <BEA-000570> <Network Configuration for Channel "AdminServer" Listen Address example.com:7002 (SSL) Public Address N/A Http Enabled true Tunneling Enabled false Outbound Enabled false Admin Traffic Enabled true ResolveDNSName Enabled false> 

Verwenden Sie den folgenden Parseausdruck:

####<(\p{Upper}\p{Lower}{2})\s+([\d]{1,2}),\s+([\d]{4})\s+([\d]{1,2}):([\d]{2}):([\d]{2})\s+(\p{Upper}{2})(?:\s+(\w+))?>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<(.*?)>\s+<\d{10}\d{3}>\s+<(.*?)>\s+<(.*?)(?:\n(.*))?>\s*

Im vorherigen Beispiel werden unter anderem die folgenden Werte vom Parseausdruck erfasst:

  • (\p{Upper}\p{Lower}{2}): Kurzname für den Monat mit 3 Buchstaben (erster Buchstabe in Großbuchstaben gefolgt von zwei Kleinbuchstaben)

  • ([\d]{1,2}): 1- oder 2-stelliger Tag

  • ([\d]{4}): 4-stelliges Jahr

  • ([\d]{1,2}): 1- oder 2-stellige Stunde

  • ([\d]{2}): Zweistellige Minute

  • ([\d]{2}): Zweistellige Sekunde

  • (\p{Upper}{2}): AM/PM-Angabe mit 2 Großbuchstaben

  • (?:\s+(\w+)): (Optional, einige Einträge geben möglicherweise keinen Wert dafür zurück) Mehrere alphanumerische Zeichen für die Zeitzone

  • (.*?): (Optional, einige Einträge geben möglicherweise keinen Wert dafür zurück) Ein oder mehrere Zeichen für den Schweregrad (in diesem Fall <INFO>)

  • (.*): Zusätzliche Details zusammen mit der Meldung

Suchmuster

In der folgenden Tabelle werden einige der häufig verwendeten Muster erläutert:

Muster Beschreibung Beispiel
. Jedes Zeichen außer Zeilenumbruch d.f entspricht def, daf, dbf usw.
* Keinmal oder mehrmals D*E*F* entspricht DDEEFF, DEF, DDFF, EEFF usw.
? Einmal oder keinmal (optional) colou?r entspricht sowohl "colour" als auch "color".
+ Ein oder mehr Stage \w-\w+ entspricht Stage A-b1_1, Stage B-a2 usw.
{2} Genau zweimal [\d]{2} entspricht 01, 11, 21 usw.
{1,2} Zwei- bis viermal [\d]{1,2} entspricht 1, 12 usw.
{3,} Dreimal oder mehr [\w]{3,} entspricht ten, hello, h2134 usw.
[ ... ] Eines der Zeichen in Klammern [AEIOU] entspricht einem Vokal in Großbuchstaben
[x-y] Eines der Zeichen im Bereich von x bis y [A-Z]+ entspricht ACT, ACTION, BAT usw.
[^x] Ein Zeichen, das nicht x ist [^/d]{2} entspricht AA, BB, AC usw.
[^x-y] Eines der Zeichen außerhalb des Bereichs von x bis y [^a-z]{2} entspricht A1, BB, B2 usw.
[\d\D] Ein Zeichen, das eine Ziffer oder keine Ziffer ist [\d\D]+ entspricht einem beliebigen Zeichen, einschließlich Zeilenvorschübe, die nicht mit dem regulären Punkt übereinstimmen
\s Ein Leerzeichen (\S+)\s+(\d+) entspricht AA 123, a_ 221 usw.
\S Ein Zeichen, das kein Leerzeichen ist (\S+) entspricht abcd, ABC, A1B2C3 usw.
\n Ein Zeilenvorschub (\d)\n(\w) entspricht:

1

A

\w Ein alphanumerisches Zeichen [\w-\w\w\w] entspricht a-123, 1-aaa usw.
\p{Lower} Kleinbuchstaben \p{Lower}{2} entspricht aa, ab, ac, bb usw.
\p{Upper} Großbuchstaben \p{Upper} entspricht A, B, C usw.
\ gefolgt von ?, [], *, . Escapezeichen (mit den Zeichen nach \ als Literale) \? gibt ? zurück