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 hat geschrieben: Eine Ausgabe auf der GUI wird nicht gemacht sondern gleich in die DB auch ohne in DB schreiben dauert es.
Dateien parsen - mit welchen Werkzeugen ?
-
- 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 ?
-
- 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 ?
Hallo mse,
Danke für den Tip - das hatte ich leider nicht gemacht.
Wie macht man es den anders ?
Gruß Frank
Danke für den Tip - das hatte ich leider nicht gemacht.
Code: Alles auswählen
Query.insert;
.
.
.
Query.post;
Query.applyupdates;
Gruß Frank
Re: Dateien parsen - mit welchen Werkzeugen ?
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.
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.
-
- 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 ?
In MSEgui würde ich TSqlite3Connection.options slo_transactions aktivieren und die Daten mit einem TSQLStatement schreiben um denDL3AD hat geschrieben: Wie macht man es den anders ?
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?
-
- 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 ?
... 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.
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.
-
- 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 ?
Vermutlich sollte TZConnection.AutoCommit auf "false" gesetzt werden.
- gladio
- Beiträge: 223
- 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 ?
Versuche es mal ohne ApplyUpdates. Das einfache Query.Post sollte reichen.
Re: Dateien parsen - mit welchen Werkzeugen ?
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.
-
- 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 ?
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
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.
-
- Beiträge: 463
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 3.6, FPC 3.2.2
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Dateien parsen - mit welchen Werkzeugen ?
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.
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.
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.
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.
-
- 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 ?
Probiere mal
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.
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;
-
- 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 ?
Hallo mse,
dein Tipp die Stringlist in einen string zu pumpen war der entscheidende Faktor.
Nun dauert es statt einer Minute nur noch eine knappe sekunde - das ist ja krass
Hallo braunbär,
Danke für das Beispiel - nun ist mir auch klar wie man im Programm mit regex umgeht.
Ich werde das heute noch ausprobieren. Der Code wird ja sehr übersichtlich
Den regex Ausdruck zu bauen ist als ungeübter jedoch eine herausforderung.
Mit den globalen Var ist mir bekannt - bei einer Function bekomme ich jedoch nur einen Wer zurück - ok man kann auch eine Array zurückgeben.
OOP klassen bauen mag ich nicht so gern - Proprerty 's kann man die auch ohne klassen verwenden ?
Gruß
Frank
dein Tipp die Stringlist in einen string zu pumpen war der entscheidende Faktor.
Nun dauert es statt einer Minute nur noch eine knappe sekunde - das ist ja krass

Hallo braunbär,
Danke für das Beispiel - nun ist mir auch klar wie man im Programm mit regex umgeht.
Ich werde das heute noch ausprobieren. Der Code wird ja sehr übersichtlich

Den regex Ausdruck zu bauen ist als ungeübter jedoch eine herausforderung.
Mit den globalen Var ist mir bekannt - bei einer Function bekomme ich jedoch nur einen Wer zurück - ok man kann auch eine Array zurückgeben.
OOP klassen bauen mag ich nicht so gern - Proprerty 's kann man die auch ohne klassen verwenden ?
Gruß
Frank
Re: Dateien parsen - mit welchen Werkzeugen ?
Hier findest du eine Version deines Parsers, die mit PChar arbeitet, und die wird in knapp 0.2 Sekunden fertig.DL3AD hat geschrieben: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.
Code: Alles auswählen
procedure GetAdifValue(var p: PChar);
var
n : integer;
Vlng: string;
begin
n := 0;
ATag := '';
AValue:= '';
Vlng := '';
while p^ <> #0 do // Wenn Zeichen nicht das abschließende Null-Byte...
begin
if p^ = '<' then
begin // Hier stehen wir auf den '<'
inc(p);//nächstes Zeichen und weiter zum Tagende, oder ein ':' kommt
while (p^ <> #0) and (p^ <> ':') and (p^ <> '>') do
begin
ATag:= ATag + p^;//gefundenes Zeichen Tag zuordnen
ATag:= Upcase(ATag);
inc(p);// Zum nächsten Zeichen.
end;
//Jetzt sind wir auf dem ':' oder haben das Tagende erreicht.
if p^ = #0 then
exit;//Fall 1) StringEnde --> raus
if p^ = ':' then//Fall 2) Wir stehen auf dem ':'
begin
inc(p);
while (p^ <> #0) and (p^ <> '>') do
begin
Vlng:= Vlng + p^;
inc(p);
end;//Jetzt sind wir auf dem '>' und haben das Tagende erreicht
n:= StrToInt(Vlng);//Länge Adif Value in Interger wandeln
inc(p);
end else
inc(p);
while (p^ <> #0) and (n > 0) do
begin
AValue:= AValue + p^;//Adif Value Zeichen aufaddieren
dec(n);
inc(p);//Von '>' weiter aud das erste Zeichen vom Adif Value
end;
if n = 0 then
exit;
end;
inc(p);
end;
end;
//Daten einlesen
procedure TForm1.Button1Click(Sender: TObject);
var
t: TDateTime;
s: String;
p: PChar;
begin
t := now;
s := AFile.Text;
p := @s[1];
while p^ <> #0 do //Ai < Length(s) do
begin
ClearVar;
while (ATag <> 'EOR') and (p^ <> #0) do
begin
ADIFPars.GetAdifValue(p);
Case ADIFPars.ATag of
'CALL' : ACALL := ADIFPars.AValue;
'QTH' : AQTH := ADIFPars.AValue;
'NAME' : ANAME := ADIFPars.AValue;
'TIME_ON' : ATIME_ON := ADIFPars.AValue;
'QSO_DATE' : AQSO_DATE := ADIFPars.AValue;
'RST_SENT' : ARST_SENT := ADIFPars.AValue;
'RST_RCVD' : ARST_RCVD := ADIFPars.AValue;
'BAND' : ABAND := ADIFPars.AValue;
'FREQ' : AFREQ := ADIFPars.AValue;
'MODE' : AMODE := ADIFPars.AValue;
'CONT' : ACONT := ADIFPars.AValue;
'ITUZ' : AITUZ := ADIFPars.AValue;
'CQZ' : ACQZ := ADIFPars.AValue;
'DXCC' : ADXCC := ADIFPars.AValue;
'COUNTRY' : ACOUNTRY := ADIFPars.AValue;
'GRIDSQUARE' : AGRIDSQUARE := ADIFPars.AValue;
'IOTA' : AIOTA := ADIFPars.AValue;
'STATE' : ASTATE := ADIFPars.AValue;
'QSL_RCVD' : AQSL_RCVD := ADIFPars.AValue;
'LOTW_QSL_RCVD': ALOTW_QSL_RCVD:= ADIFPars.AValue;
'EQSL_QSL_RCVD': AEQSL_QSL_RCVD:= ADIFPars.AValue;
'COMMENT' : ACOMMENT := ADIFPars.AValue;
'PFX' : APFX := ADIFPars.AValue;
'EOR' : ChData;
end;
inc(p);
end;
ATag:= '';
end;
ShowMessage('Fertig! (' + FormatDateTime('s.zzz', now-t) + ' s)');
end;
Noch zu einem möglichen Problem: In der ADIF-Doc, die ich weiter oben zitiert habe, steht, dass hinter der Längenangabe im Tag noch eine Datentyp-Angabe kommen kann, z.B. <qso_date:8:d>. Das bedeutet, dass die Längenangabe in diesem Code nicht richtig ausgelesen wird. Du findest sicher, wie das abzuändern ist.
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2807
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Re: Dateien parsen - mit welchen Werkzeugen ?
Du kannst auch records verwenden um mehrere Werte in einer Struktur zurückzugeben.DL3AD hat geschrieben:Mit den globalen Var ist mir bekannt - bei einer Function bekomme ich jedoch nur einen Wer zurück - ok man kann auch eine Array zurückgeben.
OOP klassen bauen mag ich nicht so gern - Proprerty 's kann man die auch ohne klassen verwenden ?
Properties ohne Klassen gibt es auch, das ist aber etwas anderes.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
-
- 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 ?
Und die Felder können binäre Daten enthalten.wp_xyz hat geschrieben: P.S.
Noch zu einem möglichen Problem: In der ADIF-Doc, die ich weiter oben zitiert habe, steht, dass hinter der Längenangabe im Tag noch eine Datentyp-Angabe kommen kann, z.B. <qso_date:8:d>. Das bedeutet, dass die Längenangabe in diesem Code nicht richtig ausgelesen wird. Du findest sicher, wie das abzuändern ist.
Code: Alles auswählen
while (p^ <> #0) and (n > 0) do //#0 kann in binären daten vorkommen.
begin
AValue:= AValue + p^;//Adif Value Zeichen aufaddieren