Dateien parsen - mit welchen Werkzeugen ?

Für Fragen von Einsteigern und Programmieranfängern...
DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von DL3AD »

Hallo,

ich habe nun eine Lösung fertig (Zeichen für Zeichen analysieren) funktioniert super aaaaber - dauer elendig lange.

Nun möchte ich noch eine andere Variante probieren - Regex ? wüste leider nicht wie ich da anfangen sollte.
Dann hatte ich mit Stringlist und Delimeter (<) probiert - es wird der String zwar zerhackt aber auch nach einen Leerzeichen und dass ist mist.

Git es irgendwo ein Beispiel wie man mit den Regex umgeht ?
Oder git es noch einen anderen Ansatz ?

Gruß
Frank

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von wp_xyz »

Such doch zuerst den Fehler in deinem aktuellen Ansatz, bevor du dich in ein neues Abenteuer stürzt. Der von mir vorgestellte Algorithmus ist garantiert schnell. Wahrscheinlich schreibst du die extrahierten Strings in visuelle Controls, die sich bei jeder Änderung neu ausgeben. Um mehr sagen zu können, müsste ich deinen Code kennen.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von DL3AD »

... hier die Procedure die mir einen Tag und Tagvalue extrahiert

Code: Alles auswählen

//ADIF Tag und Tag Value ermitteln
procedure GetAdifValue(Txt: string);
var
  n   : integer;
  Vlng: string;
begin
  n     := 0;
  ATag  := '';
  AValue:= '';
  Vlng  := '';
  while Ai <= Length(Txt) do //Wenn index kleiner als Textlänge
  begin
    if Txt[Ai] = '<' then
      begin // Hier stehen wir auf den '<'
        inc(Ai);//nächstes Zeichen und weiter zum Tagende, oder ein ':' kommt
        while (Ai <= Length(Txt)) and  (Txt[Ai] <>':') and (Txt[Ai] <> '>') do
          begin
            ATag:= ATag + Txt[Ai];//gefundenes Zeichen Tag zuordnen
            ATag:= Upcase(ATag);
            inc(Ai);// Zum nächsten Zeichen.
          end;//Jetzt sind wir auf dem ':' oder haben das Tagende erreicht.
          if Ai > Length(Txt) then exit;//Fall 1) StringEnde --> raus
          if Txt[Ai] = ':' then//Fall 2) Wir stehen auf dem ':'
            begin
              inc(Ai);
              while (Ai <= Length(Txt)) and (Txt[Ai] <> '>') do
                begin
                  Vlng:= Vlng + Txt[Ai];
                  inc(Ai);
                end;//Jetzt sind wir auf dem '>' und haben das Tagende erreicht
              n:= StrToInt(Vlng);//Länge Adif Value in Interger wandeln
            end;
              while (Ai <= Length(Txt)) and (n > 0) do
              begin
                inc(Ai);//Von '>' weiter aud das erste Zeichen vom Adif Value
                AValue:= AValue + Txt[Ai];//Adif Value Zeichen aufaddieren
                dec(n);
              end;
              inc(Ai);//Vom letzen Zeichen des Adif Value auf das nächse gehen
              if n = 0 then exit;
       end;
  inc(Ai);
  end;
end;
 


Die einzelnen TagValue werden über eine Case entsprechnden Variablen zugeordnet bis ein Datensatz komplett ist (wenn Tag <eor>) einige Tags werden auch verworfen weil sie nicht gebraucht werden.
Eine Ausgabe auf der GUI wird nicht gemacht sondern gleich in die DB auch ohne in DB schreiben dauert es.

Der Ansatz von Braunbär scheint recht interessant nur weis ich nicht wie man mit den Regexp umgehen kann .

Gruß
Frank

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von Mathias »

ich habe nun eine Lösung fertig (Zeichen für Zeichen analysieren) funktioniert super aaaaber - dauer elendig lange.

Wieso sollte dies sehr lange dauern, eigentlich müsste dies die schnellste Lösung sein.
Alle anderen Funktion Splitt, Pos, etc. machen im Hintergrund nichts anderes, habe aber viel Overhead, da sie universell sind.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von mse »

DL3AD hat geschrieben:Eine Ausgabe auf der GUI wird nicht gemacht sondern gleich in die DB auch ohne in DB schreiben dauert es.

Falls du in Sqlite3 schreibst sollten die Records in einer gemeinsamen Transaktion geschrieben werden, sonst wird bei jedem INSERT gesynct und das ist langsam.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von DL3AD »

Hallo mse,

Danke für den Tip - das hatte ich leider nicht gemacht.

Code: Alles auswählen

Query.insert;
.
.
.
Query.post;
Query.applyupdates;


Wie macht man es den anders ?

Gruß Frank

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von wp_xyz »

Nochmals visuelle Controls: Hängen am Query visuelle DBControls? Ein DBGrid z.B.? Hier bringt Query.DisableControls vor und Query.EnableControls nach der gesamten Aktion Wunder.

Welche Datenbank?

Ein Tipp zum Prüfen, ob GetADIFValue der Sündenbock ist: Kommentiere einmal den Zugriff auf die Datenbank aus, so dass GetADIF Value die gefundenen Daten ins Leere schreibt. Das muss sehr schnell gehen. Wenn du das bestätigen kannst, sollte klar sein, dass RegEx auch nicht schneller sein kann.

Wenn nicht weiterhilft, müsstest du den gesamten Code posten.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von mse »

DL3AD hat geschrieben:Wie macht man es den anders ?

In MSEgui würde ich TSqlite3Connection.options slo_transactions aktivieren und die Daten mit einem TSQLStatement schreiben um den
TSQLQuery overhead zu vermeiden.
In Lazarus kannst du etwas entsprechendes mit TSQLQuery.ExecSQL() erreichen, eine SQL-Statement Komponente gibt es AFAIK mittlerweile auch. Die SQL-property muss ein SQL-INSERT-Statement enthalten, welches die Werte in die Datenbank schreibt. Etwa so:

Code: Alles auswählen

 
 while datenvorhanden do begin
  werte nächsten Record aus;
  speichere die Felddaten in die entsprechenden "<sqlquery>.Params" Items;
  <sqlquery>.execsql();
 end;
 <transaction>.commit();
 

Der implizite-Transaktions-Modus muss ausgeschaltet sein, damit nicht bei jedem execsql() commited wird. Wie das in FPC geht, weiss ich nicht.
Kennst du dich mit SQL-INSERT statements aus?
Wie lange dauert der Schreibvorgang? Bei wie vielen records?

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von DL3AD »

... ich verwende die Zeos komponenten für meine sqlite3 DB
In der Query habe ich CachedUpdates aktiviert damit nicht sofort in die DB geschrieben wird und mit ApplayUpdates wird in die DB geschrieben.

Werde mal rumsuchen wie man das mit den Zeos lösen kann.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von mse »

Vermutlich sollte TZConnection.AutoCommit auf "false" gesetzt werden.

Benutzeravatar
gladio
Beiträge: 217
Registriert: Sa 21. Jun 2014, 06:15
OS, Lazarus, FPC: Win10-64 - aktuelle Lazarus/FPC Standard-Edition
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von gladio »

Versuche es mal ohne ApplyUpdates. Das einfache Query.Post sollte reichen.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von wp_xyz »

Nimm dir die Zeit und extrahiere die fraglichen Programmteile (parsen und in DB schreiben) in eine kleine Demo, so dass der Fehler reproduziert werden kann, und poste das Projekt zusammen mit der ADIF-Datei. Wahrscheinlich findest du dabei den Fehler schon von allein, oder du bekommst hier innerhalb kürzester Zeit konkrete Hinweise, was falsch ist. Sonst bleibt hier alles beim unverbindlichen Herumraten.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von DL3AD »

Hallo wp_xyz,

im Dateianhang ein Auszug aus dem Projekt.
Es ist nur das Parsen und zuordnen zu den DB Feldern ohne DB schreiben.
Es dauer ewig bis er fertig hat.
Die Adif mit ca 2500 Datensätzen ist auch dabei.

Gruß
Frank
Zuletzt geändert von DL3AD am Mi 12. Jul 2017, 10:17, insgesamt 1-mal geändert.

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von braunbär »

Um es vorauszuschicken: Mit Regex wird das Parsing ziemich sicher nicht schneller. Ich denke aber, dass der Zeitverbrauch des Parsens bei deiner Aufgabe vernachlässigbar ist, egal, welches System du verwendest, Das Laufzeitproblem ist irgendwo anders begründet.
Der Vorteil von Regex liegt in der höheren Flexibilität, in der Einfacheit des Programms und in der Direktheit des Ansatzes. Statt einen Algorithmus zu programmieren, der die vorgegebene Syntax auflöst, schreibe ich ein Regex, dessen Komponenten nur beschreiben, wie der gesuchte String ausschaut. Um Details der Implementierung der Matchings brauche ich mich dann nicht zu kümmern, und wenn sich irgend wann an den Anforderugen etwas ändert, brauche ich nicht in Code wühlen, um zu finden, was ich jetzt in meinem Algorithmus anders machen muss, sondern ich brauche nur den regulären Ausdruck dort ändern, wo sich die Vorgabe geändert hat.

Code: Alles auswählen

 
uses
  RegExpr;
 
...
 
procedure GetAdifValue(var Txt: string);
const
regex =
   '(?i)'       // Case-insensitive
  +'^<'         // Der String muss mit dem Zeichen < anfangen
  +'(?:'        // Was hier innerhalb der Klammern folgt, ist als Submatch uninteressant. Die Klammern sind nur dazu da, um die beiden Alternativen zu klammern
  +'(eor)>\s*'  // 1. Alternative: Der Text eor>, wobei "eor" als erster Submatch gespeichert wird. Eventuelle Leerzeichen u.dgl. nach dem > werden ignoriert
  +'|'          // Hier endet die erste Alternative und beginnt die zweite Alternative
  +'([^:]+)'    // 1 oder mehr beliebige Zeichen bis zu einem ":" , wird im zweiten Submatch gespeichert
  +':'          // Dann ein Doppelpunkt
  +'([^>]+)'    // 1 oder mehr beliebige Zeichen bis zu einem ">" , wird im dritten Submatch gespeichert. Anzahl der Zeichen des Datenfelds   
  +'>'          // Das Zeichen >
  +'([^<]+)'    // 1 oder mehr beliebige Zeichen bis zu einem < oder dem Stringende, wird im vierten Submatch gespeichert,
                // eventuell statt dessen '(.{\3})\s*' probieren, wenn in den Daten das <-Zeichen vorkommen kann
  +')';         //  Schließt die Klammer von "(?:" - hier endet die zweite Alternative
 
var
re: TRegExpr;
begin
re := TRegExpr.Create(regex);
try
  if re.Exec(txt) then
  begin
   ATag:=upcase(trim(re.Match2));
   AValue:=upcase(trim(re.Match4));
   delete(txt,1,length(re.Match));
  end
  else showmessage('Syntax passt nicht: '+Txt);
  finally re.free;
end.
 


Die Prozedur schneidet aus dem String den gefundenen Teil heraus, sodass der nächste Prozeduraufruf richtig weitermacht.
Wenn der Tag eor gematcht wird, wird ein leerer Tag als ATag gesetzt (eor liegt in re.Match[1] und nicht in re.Match[2]).
Es wird alles in Upper Case konvertiert, wenn das bei den Daten unerwünscht ist, lass das upcase dort weg.
An sich gehören aTag und aValue als Prozedurparameter übergeben, globale Variable dafür zu nehmen ist prinzipiell keine gute Idee.

Eventuell wäre statt des Showmessage eine Exception angebracht, weil ja nicht sinnvoll weitergematcht werden kann.
Etwas effizienter wird es, wenn man das Regex nur einmal außerhalb dieser Prozedur erzeugt und erst ganz am Ende wieder freigibt.
Zuletzt geändert von braunbär am Mo 10. Jul 2017, 11:56, insgesamt 2-mal geändert.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Dateien parsen - mit welchen Werkzeugen ?

Beitrag von mse »

Probiere mal

Code: Alles auswählen

 
var
 s1: string;
begin
 s1:= AFile.Text;
  while Ai < Length(s1) do
  begin
    ClearVar;
    while (ATag <> 'EOR') and (Ai <= Length(s1)) do
      begin
        ADIFPars.GetAdifValue(s1);
        Case ADIFPars.ATag of
          'CALL'         : ACALL         := ADIFPars.AValue;
 

TStringlist.text ist eine sehr teure Operation da dort der String immer aus allen Zeilen zusammengesetzt wird. Wenn die Geschwindigkeit nicht reicht, gibt es noch viele weitere Optimierungsmöglichkeiten.

Antworten