RegExp: Entweder/Oder matchen, und 'False' zurückgeben wenn kein Match

  • Hallo!

    Ich habe ein Problem. Und zwar möchte ich eine recht große Datei parsen, was aber mit meiner Methode (Zeile für Zeile mit Regex parsen) ziemlich langsam und CPU-fressend ist. Ich frage mich darum ob es da nicht einen effektiveren Weg gibt, zb indem man mit einem einzigen RegExp (mit globalem Setting) gleich alle Infos aus der Datei zieht.

    Ich habe dazu folgendes Script bisher:

    Spoiler anzeigen
    [autoit]

    GetJobsList()

    [/autoit] [autoit][/autoit] [autoit]

    Func GetJobsList() ; Jobs-Datei parsen
    $file_hwnd = FileOpen(@ScriptDir & '\vdub.txt',0)
    $file_read = FileRead($file_hwnd)

    [/autoit] [autoit][/autoit] [autoit]

    ; Immer vorhanden - kein Problem
    $para01 = '.*?\$job "(.*?)"\r' ; $job "xxx"
    $para02 = '.*?\$input "(.*?)"\r' ; $input "xxx"
    $para03 = '.*?\$state (\d*)\r' ; state x
    ; Nicht immer vorhanden - wenn nicht, hätt ich gern einen Platzhalter (zb False) im Array.
    $para04 = '.*?\$error "(.*?)"\r' ; $error "xxx"
    ; Entweder 05 ODER 06
    $para05 = '.*?ExportViaEncoderSet\("(.*?)", "(.*?)"\);\r' ; ExportViaEncoderSet("xxx","xxx");
    $para06 = '.*?SaveAVI\("(.*?)"\);\r' ; SaveAVI("xxx");

    [/autoit] [autoit][/autoit] [autoit]

    $regexp_array = StringRegExp($file_read,'(?s)(?m)' & $para01 & $para02 & $para03,3)

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($regexp_array)

    [/autoit] [autoit][/autoit] [autoit]

    FileClose($file_hwnd)
    EndFunc

    [/autoit]

    Die Probleme beginnen ab Para04. Diese Zeile existiert nicht immer in der Datei. Wenn nicht, dann hätt ich gern irgendwie trotzdem eine Rückgabe, zb "False". Oder zumindest dass die Suche weitergeht, denn wenn er keine Error-Zeile findet, dann sprengt dass das ganze RegExp und es wird nicht korrekt weitergematcht.

    Para 05 und 06 sind ein anderes Problem.. denn es gibt entweder die Zeile von Para 05, oder die von 06 pro Textblock. Ich weiß nur dass man mit '(oder1|oder2)' per RegExp "oder" machen kann, aber das betrifft dann nur die Matches, während ich hier gleich eine ganze Zeile "odern" muss ohne aber die komplette Zeile zu matchen, sondern nur die beiden (bei 05 sind's zwei Matches) oder den einen (bei 06 nur einer) Strings in den Anführungsstrichen halt...

    Geht das überhaupt? Oder ist mein Ansatz total blöd?

    Davor hatte ich _FileReadToArray und hab jede Zeile einzeln geregext und die Treffer in ein 6-dimensionales Array gepackt. Das hat funktioniert ist aber wie gesagt wirklich sehr langsam. Da ich Datei überwache wäre dass nicht so optimal...

    Die zu parsende Datei hab ich mal in den Anhang gepackt...
    autoit.de/wcf/attachment/21387/

    Vielleicht hat ja der ein oder andere Lust mir zu helfen. Würd mich freuen.

  • So,
    habe mich mal daran versucht.

    denke es sollte so funktionieren

    Spoiler anzeigen
    [autoit]

    #region ;************ Includes ************
    #include <String.au3>
    #include <Array.au3>
    #endregion ;************ Includes ************

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    GetJobsList()

    [/autoit] [autoit][/autoit] [autoit]

    Func GetJobsList($log_func = True) ; Jobs-Datei parsen
    If $log_func Then _Log('Parse Jobs-Datei')

    [/autoit] [autoit][/autoit] [autoit]

    $file_hwnd = FileOpen(@ScriptDir & '\vdub.txt', 0)
    $file_read = FileRead($file_hwnd)

    [/autoit] [autoit][/autoit] [autoit]

    ; Immer vorhanden - kein Problem
    $para01 = '.*?\$job "(.*?)"\r' ; $job "xxx"
    $para02 = '.*?\$input "(.*?)"\r' ; $input "xxx"
    $para03 = '.*?\$state (\d*)\r' ; state x
    ;~ ; Nicht immer vorhanden - wenn nicht, hätt ich gern einen Platzhalter (zb False) im Array.
    $para04 = '.*?(?:\$error |\$reloadstop )(.*?)\r' ; $error "xxx"
    ; Entweder 05 ODER 06
    $para05 = '.*?(?:ExportViaEncoderSet|SaveAVI)(.*?)\r'

    [/autoit] [autoit][/autoit] [autoit]

    $regexp_array = StringRegExp($file_read, '(?s)(?m)' & $para01 & $para02 & $para03 & $para04 & $para05, 3)

    [/autoit] [autoit][/autoit] [autoit]

    FileClose($file_hwnd)

    [/autoit] [autoit][/autoit] [autoit]

    ;Array richtiggstellen
    For $i = 3 To UBound($regexp_array) - 1 Step 5
    If $regexp_array[$i] = "--" Then
    $regexp_array[$i] = "False"
    Else
    $regexp_array[$i] = StringTrimLeft($regexp_array[$i], 1)
    $regexp_array[$i] = StringTrimRight($regexp_array[$i], 1)
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($regexp_array)
    EndFunc ;==>GetJobsList

    [/autoit]

    Hab ein bisschen tricksen müssen, gerade dort wo du "False" stehen haben willst. Sollte aber passen :D
    "$reloadstop" habe ich nur verwendet, da dies immer existiert. Dies wird dann gefunden, wenn "error" nicht existiert. Könnte auch ein anderer Eintrag sein, jedoch muss er nach "error" und vor "ExportViaEncoderSet" bzw. "SaveAVI" stehen.

  • Wow, es funktioniert! Da ich die Funktion praktisch täglich sehr häufig laufen werden hab, ist dir mein langanhaltender Dank gewiss :D
    Und grad in der Schleife von einer Sekunde testet - ganz kurz nur 0,3% CPU Auslastung. Bei meiner ersten Methode die alles erfolgreich geparst hat (mit vielen If-Bedingungen und @error etc) hatte ich permanent 4% Auslastung bei ner 1-Sek Schleife.

    Ich werd damit mal noch rumspielen um genau zu durchblicken was du da getrieben hast, falls da dann Fragen auftauchen werd ich die noch stellen, aber ich bin jetzt erstmal grad ganz euphorisch dass es klappt weil echt mein ganzer Samstag dafür erfolglos drauf ging. :thumbup:

    Edit: Ok, hab's implementiert, verstanden was du gemacht hast und das ganze noch etwas perfektioniert. Die coolste Erkenntnis ist das '?:' anscheinend Dinge in Runden klammert nicht matcht, das ist sehr gut zu wissen und nützlich. Um noch weiter Ressourcen zu schonen parse ich jetzt nurnoch die Datei wenn sich ihr Änderungsdatum ändert, nur so nebenbei noch erwähnt :D Dickes Danke nochmal.

    3 Mal editiert, zuletzt von klischee (27. Mai 2013 um 00:55)