Import eines "wilden" Textfiles in SQLite

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
HB9FIH
Beiträge: 11
Registriert: Mi 20. Jul 2022, 10:45
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Wohnort: 8222 Beringen (CH)
Kontaktdaten:

Import eines "wilden" Textfiles in SQLite

Beitrag von HB9FIH »

Ich frage hier weil ggf jemand eine bessere Idee hat und sag schon mal Danke für eine andere Idee.
Hier ist eine (text)-Datei die ich in einer SQlite Tabelle importieren will.
Das ist eine sog. ADI / ADIF Datei. (benutzt im Amateurfunk).
Unten gebe ich den Header und die 2 Records . Die Records beginnen nach dem <EOH> (der Header ist nicht interessant) mit "<app_qrzlog_logid:" und ein Record ist beendet mit "<eor>", dann kommt der nächste..

Als alter dbf Benutzer würde das in eine Stringlist einfügen, dann Records aus dem bilden, dh vom Beginn bis zum Ende einen Record.
Dann aus diesen Records die Daten in einzelne Felder umfüllen.
Ich möchte es diesmal aber in SQLite haben (da ich eben in das eingestiegen bin und damit Erfahrung sammeln will)
Weil dann mit select die erforderlichen Daten gut extrahiert werden können wie zB wieviele verschiedene DXCC, band, gridsquare etc.
Doch es soll eine Apllikation werde die wir in userem Club einsetzen.

Weiter in Gedanken:
Würde, da man nicht alle Felder braucht eine FeldListe für den Import und eine FeldListe für die SQLIte db machen, auch damit die selects standardisiert sind (da nicht jeder SQL kann).
zB <band: --> BAND, <gridsquare: --> HIS_GRID, <my_gridsquare: --> MY_GRID etc (aus den GRID's kann man die Distanz rechnen)

QRZLogbook download for hb9fih
Date: Fri Jul 29 06:26:46 2022
Bookid: 19332
Records: 204
<ADIF_VER:5>3.1.1
<PROGRAMID:10>QRZLogbook
<PROGRAMVERSION:3>2.0
<eoh>
<app_qrzlog_logid:9>717291809
<app_qrzlog_qsldate:8>20220214
<app_qrzlog_status:1>C
<band:3>20m
<band_rx:3>20m
<call:5>9K2YD
<comment:9>QTH EA8 -
<cont:2>AS
<country:6>Kuwait
<cqz:2>21
<distance:4>6363
<dxcc:3>348
<email:23>sailor2251970@gmail.com
<eqsl_qsl_rcvd:1>N
<eqsl_qsl_sent:1>N
<freq:6>14.074
<freq_rx:6>14.074
<gridsquare:8>LL49AH17
<ituz:2>39
<lat:11>N029 19.483
<lon:11>E048 00.615
<lotw_qsl_rcvd:1>N
<lotw_qsl_sent:1>N
<mode:3>FT8
<my_city:3>EA8
<my_country:11>Switzerland
<my_cq_zone:2>14
<my_gridsquare:6>IL07xs
<my_itu_zone:2>28
<my_lat:11>N027 46.248
<my_lon:11>W018 02.520
<my_name:12>Erich Rieder
<name:22>Younes M H H ALMATROOK
<qrzcom_qso_upload_date:8>20220115
<qrzcom_qso_upload_status:1>Y
<qsl_rcvd:1>N
<qsl_sent:1>N
<qso_date:8>20220107
<qso_date_off:8>20220107
<qth:7>ALSHAAB
<rst_rcvd:3>599
<rst_sent:3>599
<station_callsign:6>HB9FIH
<time_off:4>1550
<time_on:4>1550
<tx_pwr:2>15
<eor>

<app_qrzlog_logid:9>722339967
<app_qrzlog_qsldate:8>20220214
<app_qrzlog_status:1>C
<band:3>40m
<band_rx:3>40m
<call:5>SP8LM
<comment:10>QTH EA8 -
<cont:2>EU
<country:6>Poland
<cqz:2>15
<distance:4>1042
<dxcc:3>269
<email:11>sp8lm@wp.pl
<eqsl_qsl_rcvd:1>N
<eqsl_qsl_sent:1>N
<freq:5>7.076
<freq_rx:5>7.076
<gridsquare:6>KN19FI
<ituz:2>28
<lat:11>N049 22.233
<lon:11>E022 25.167
<lotw_qsl_rcvd:1>N
<lotw_qsl_sent:1>N
<mode:4>JT65
<my_city:15>8820 Wädenswil
<my_country:11>Switzerland
<my_cq_zone:2>14
<my_gridsquare:6>JN47if
<my_itu_zone:2>28
<my_lat:11>N047 14.100
<my_lon:11>E008 40.933
<my_name:12>Erich Rieder
<name:21>Marek Jan Łagowski
<qrzcom_qso_upload_date:8>20220129
<qrzcom_qso_upload_status:1>Y
<qsl_rcvd:1>N
<qsl_sent:1>N
<qso_date:8>20220127
<qso_date_off:8>20220127
<qth:12>Polańczyk
<rst_rcvd:3>599
<rst_sent:3>599
<station_callsign:6>HB9FIH
<time_off:4>2105
<time_on:4>2105
<tx_pwr:2>20
<eor>

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Import eines "wilden" Textfiles in SQLite

Beitrag von af0815 »

Ich würde das ganze Zeilenweise parsen den Header auswerten und dann für jeden Datensatz eine Struktur parsen, weil soweit ich sehe besteht eine Zeile aus einen Header einer Zahl und den Daten. Das kann man dann auch in csv oder xml oder json umkodieren oder auch ganz einfach in eine Tabelle schreiben, wenn die Struktur immer gleich ist. Wenn nicht so kann man aus dem Header und der Zahl einen Schlüssel bilden und den zusammen mit einer Datensatzkennung in der Tabelle abspeichern, die wird dann viele Datensätze beinhalten, aber jeder Datensatz ist eindeutig adressier und zuordenbar. Klingt komplizierter als es ist :-) Aber ich mache solche Sachen öfters.

Zum Zeilenweisen einlesen verwendet ich folgendes

Code: Alles auswählen

function ReadTextFirstLines(const FileName: string; MaxLines: integer;
  TS: TStrings): boolean;
var
    TFS : TTextFileStream;
    i: integer;
begin
  Result:= false;
  if (not assigned(TS)) or (MaxLines < 1) or (FileName = '') then exit;
  TFS := TTextFileStream.Create(FileName,fmOpenRead);
  try
    i := 0;
    while (not TFS.Eof) and (i < MaxLines ) do
    begin
      TS.Append(TFS.ReadLn);
      inc(i);
    end;
    Result:= true;
  finally
    TFS.Free;
  end;
end;

Der TTextFileStream findest du in der angehängten Datei.

Das Beispiel soll nur zeigen, wie du Zeile für Zeile mittels TFS.Readln den Stream komfortabel bekommst. In dem Beispiel kannst du zum Beispiel eine ganze Datei in einen TStrings hineinladen und dann wo anders die Zeile Parsen.
Dateianhänge
utextfilestream.pas
(4.56 KiB) 24-mal heruntergeladen
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10468
Registriert: Mo 11. Sep 2006, 19:01

Re: Import eines "wilden" Textfiles in SQLite

Beitrag von theo »

Oder so (Quick and Dirty, aber für überschaubare Datenmengen nicht unbedingt falsch für den einmaligen Import. Muss man bestimmt noch an die Gegebenheiten anpassen)

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  SL, WL: TStringList;
  SA: TStringArray;
  i, j: integer;
  Columns, Values: string;
begin
  SL := TStringList.Create;
  WL := TStringList.Create;
  SL.LoadFromFile('/home/theo/adidata.txt'); //Hier sind die Daten drin
  SA := Sl.Text.Split(['<eoh>'], TStringSplitOptions.ExcludeEmpty);
  Memo1.Lines.Add(Trim(SA[0]));
  Memo1.Lines.Add('-------- END HEADER --------');
  SA := SA[1].Split(['<eor>'], TStringSplitOptions.ExcludeEmpty);
  for i := 0 to Length(SA) - 1 do
  begin
    WL.Text := StringReplace(Trim(SA[i]), '<', '', [rfReplaceAll]);
    WL.NameValueSeparator := '>';
    Columns := '';
    Values := '';
    for j := 0 to WL.Count - 1 do
    begin
      Columns += WL.Names[j].Split(':')[0];
      Values += QuotedStr(WL.ValueFromIndex[j]);         
      if j < WL.Count - 1 then
      begin
        Values += ',';
        Columns += ',';
      end;
    end;
    Memo1.Lines.Add('INSERT INTO table_name (' + Columns + ')');
    Memo1.Lines.Add('VALUES (' + Values + ');');
  end;
  WL.Free;
  SL.Free;
end;     
Sieht dann so aus:
QRZLogbook download for hb9fih
Date: Fri Jul 29 06:26:46 2022
Bookid: 19332
Records: 204
<ADIF_VER:5>3.1.1
<PROGRAMID:10>QRZLogbook
<PROGRAMVERSION:3>2.0
-------- END HEADER --------
INSERT INTO table_name (app_qrzlog_logid,app_qrzlog_qsldate,app_qrzlog_status,band,band_rx,call,comment,cont,country,cqz,distance,dxcc,email,eqsl_qsl_rcvd,eqsl_qsl_sent,freq,freq_rx,gridsquare,ituz,lat,lon,lotw_qsl_rcvd,lotw_qsl_sent,mode,my_city,my_country,my_cq_zone,my_gridsquare,my_itu_zone,my_lat,my_lon,my_name,name,qrzcom_qso_upload_date,qrzcom_qso_upload_status,qsl_rcvd,qsl_sent,qso_date,qso_date_off,qth,rst_rcvd,rst_sent,station_callsign,time_off,time_on,tx_pwr)
VALUES ('717291809','20220214','C','20m','20m','9K2YD','QTH EA8 -','AS','Kuwait','21','6363','348','sailor2251970@gmail.com','N','N','14.074','14.074','LL49AH17','39','N029 19.483','E048 00.615','N','N','FT8','EA8','Switzerland','14','IL07xs','28','N027 46.248','W018 02.520','Erich Rieder','Younes M H H ALMATROOK','20220115','Y','N','N','20220107','20220107','ALSHAAB','599','599','HB9FIH','1550','1550','15');
INSERT INTO table_name (app_qrzlog_logid,app_qrzlog_qsldate,app_qrzlog_status,band,band_rx,call,comment,cont,country,cqz,distance,dxcc,email,eqsl_qsl_rcvd,eqsl_qsl_sent,freq,freq_rx,gridsquare,ituz,lat,lon,lotw_qsl_rcvd,lotw_qsl_sent,mode,my_city,my_country,my_cq_zone,my_gridsquare,my_itu_zone,my_lat,my_lon,my_name,name,qrzcom_qso_upload_date,qrzcom_qso_upload_status,qsl_rcvd,qsl_sent,qso_date,qso_date_off,qth,rst_rcvd,rst_sent,station_callsign,time_off,time_on,tx_pwr)
VALUES ('722339967','20220214','C','40m','40m','SP8LM','QTH EA8 -','EU','Poland','15','1042','269','sp8lm@wp.pl','N','N','7.076','7.076','KN19FI','28','N049 22.233','E022 25.167','N','N','JT65','8820 Wädenswil','Switzerland','14','JN47if','28','N047 14.100','E008 40.933','Erich Rieder','Marek Jan Łagowski','20220129','Y','N','N','20220127','20220127','Polańczyk','599','599','HB9FIH','2105','2105','20');

HB9FIH
Beiträge: 11
Registriert: Mi 20. Jul 2022, 10:45
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Wohnort: 8222 Beringen (CH)
Kontaktdaten:

Re: Import eines "wilden" Textfiles in SQLite

Beitrag von HB9FIH »

Danke sehr - und erst noch ohne dbf ...
Ich mache mal beides ..

HB9FIH
Beiträge: 11
Registriert: Mi 20. Jul 2022, 10:45
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: 64Bit
Wohnort: 8222 Beringen (CH)
Kontaktdaten:

Re: Import eines "wilden" Textfiles in SQLite

Beitrag von HB9FIH »

@Theo - ich danke Dir... selbstverständlich auch dem anderen Helfer.
habe dies nun etwas erweitert. Den Header braucht man nicht den kann man vorgängig in einem Text Editor eliminieren.
Interessant war dass mit dem einlesen des header immer nur 2 QSO0s aufbereitet wurden - aber das ist gelöst.
Habe mal Lots von 204 und 150 QSO's eingelesen, was sehr schnell geht und habe sie nun in der DB:
Mit flexiblen SELECTS, auch DISTINC kann ich nun auswerten was ich will.
Ich baue nun noch etwas "rundum Komfort" und Sicherheit ein.

Ganz am Ende des erstellten SQL gibt es noch einen leeren INSERT, das dann auf Fehler läuft, den muss ich noch eliminieren.

Hier noch, von Deiner Routine, meine etwas erweiterte Form:

Code: Alles auswählen

procedure TForm1.btnMEINClick(Sender: TObject);
  var
     SL, WL: TStringList;
     SA: TStringArray;
     i, j, ii : integer;
     Columns, Values: string;
     MyCursor: TCursor;
begin
     MyCursor := Self.Cursor;
     Self.Cursor := crHourGlass;

     memo2.clear;
     SL := TStringList.Create;
     WL := TStringList.Create;
     SL.LoadFromFile(workfile); //Hier sind die Daten drin



{
     SA := Sl.Text.Split(['<eoh>'], TStringSplitOptions.ExcludeEmpty);
     Memo2.Lines.Add(Trim(SA[0]));
     Memo2.Lines.Add('-------- END HEADER --------');
     SA := SA[1].Split(['<eor>'], TStringSplitOptions.ExcludeEmpty);
}
{Info
 Habe daas von QRZ exportierte ADI File mutiert, dh ich habe den Header rausgelöscht
 Den braucht man nicht
}



SA := SL.Text.Split(['<eor>'], TStringSplitOptions.ExcludeEmpty);

memo2.Append(SL.text);

ii := Length(SA);
showmessage('Anzahl QSO: ' + IntToStr(ii));
Self.Cursor := crHourGlass;

memo2.Clear;
//for ii := 0 to Length(SA) -1 do
    begin
//       SA := SA[0].Split(['<eor>'], TStringSplitOptions.ExcludeEmpty);
// Split wurde oben zl 255 schon gemacht
         for i := 0 to Length(SA) - 1 do
         begin
              WL.Text := StringReplace(Trim(SA[i]), '<', '', [rfReplaceAll]);
              WL.NameValueSeparator := '>';
              Columns := '';
              Values := '';
              for j := 0 to WL.Count - 1 do
              begin
              Columns += WL.Names[j].Split(':')[0];
              Values += QuotedStr(WL.ValueFromIndex[j]);
              if j < WL.Count - 1 then
                 begin
                 Values += ',';
                 Columns += ',';
                 end;
              end;
         Memo2.Lines.Add('INSERT INTO table_name (' + Columns + ')');
         Memo2.Lines.Add('VALUES (' + Values + ');');
         end;
    end;

     WL.Free;
     SL.Free;
     Memo2.Lines.SaveToFile('xM2-dxcc.txt');
     Self.Cursor := MyCursor;
end;    

Antworten