Autor Beitrag
probare
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mi 04.12.13 15:38 
Hi,

ich lese eine Textdatei in eine Listbox (ListBoxRead) ein.
Bei einem Klick auf einen Button (btnRead) möchte ich nun jede einzelne Zeile der Liste auf einen bestimmten String prüfen und speziell behandeln.

Folgend erst einmal ein Auszug aus dem Quellcode - wie er bisher war:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure TfrmMain.btnReadClick(Sender: TObject);

var
i : Integer;
...

begin
...
   for i := 0 to ListBoxRead.Items.Count -1 do
   begin 

   if ListBoxRead.Items[i] := '## 123'
      begin
      ...
      end;

   if ListBoxRead.Items[i] := '## 234'
      begin
      ...
      end;
    ...
   end;
end;


Das funktioniert für kleine Textdateien zwar wunderbar, aber für größere ist der Rechenaufwand aufgrund der ganzen if-Anweisungen (sind natürlich nicht nur 2, sondern in diesem Fall eine ganze Menge) ziemlich hoch.

Verbesserung des Rechenaufwands kann hier die case-Anweisung schaffen, wenn ich mich richtig informiert habe.
Leider bekomme ich das in diesem Fall nicht so ganz auf die Reihe und dementsprechend Fehlermeldungen.
Eventuell kann mir hier ja jemand helfen.
Ich poste kurz meine Gehversuche:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
for i := 0 to ListBoxRead.Items.Count -1 do
begin
...
   case (ListBoxRead.Items[i]) of //<- Ordinaltype required
       '## 123' :                //<- Incompatible types 'Integer' and 'String'
           begin
           ...
           end;

       '## 234' :
           begin
           ...
           end;
end;



Wie funktioniert es richtig?

Moderiert von user profile iconMartok: Quote- durch Delphi-Tags ersetzt
baumina
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 305
Erhaltene Danke: 61

Win 7
Delphi 10.2 Tokyo Enterprise
BeitragVerfasst: Mi 04.12.13 15:54 
Case geht mit Strings nicht. Aber wenn du innerhalb deiner if's ein continue einbaust, muss er sobald er das richtige if gefunden hat nicht alle anderen if#s noch prüfen, sondern springt in deiner for-Schleife gleich weiter.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
if ListBoxRead.Items[i] := '## 123' 
 begin 
 ... 
   continue;
 end;

Für diesen Beitrag haben gedankt: probare
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mi 04.12.13 15:57 
user profile iconbaumina hat folgendes geschrieben Zum zitierten Posting springen:
Case geht mit Strings nicht. Aber wenn du innerhalb deiner if's ein continue einbaust, muss er sobald er das richtige if gefunden hat nicht alle anderen if#s noch prüfen, sondern springt in deiner for-Schleife gleich weiter.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
if ListBoxRead.Items[i] := '## 123' 
 begin 
 ... 
   continue;
 end;


Herzlichen Dank, hilft mir schonmal sehr.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 04.12.13 16:10 
Case funktioniert nur mit ordinalen Datentypen, d.h. Typen, die einen klar definierten Vorgänger und Nachfolger besitzen. Dazu gehören ganze Zahlen und Zeichen, aber keine Strings. Etwas komfortabler ginge es evtl. noch mit AnsiIndexText.

Für diesen Beitrag haben gedankt: probare
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 04.12.13 17:26 
... und wenn in deinen Strings ausschließlich "Zahlenwerte" stehen, wandelst du die Strings in einen Integer um, was dich in die Lage versetzt, doch noch einen Case-Block einzusetzen.
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Mi 04.12.13 17:33 
Mit AnsiIndexText ist man ja auch in der Lage, case zu verwenden:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
uses StrUtils;

const
  POSSIBLEVALUES: array[0..4of string = (
    '## 123',
    '## 234',
    '## 345',
    '## 456',
    '## 567');

procedure TFormTest.ButtonTestClick(Sender: TObject);
begin
  case AnsiIndexText('## 345', POSSIBLEVALUES) of
    -1:
      ShowMessage('Nicht enthalten');
    0:
      ShowMessage('Erster');
    1:
      ShowMessage('Zweiter');
    2:
      ShowMessage('Dritter');
    3:
      ShowMessage('Vierter');
    4:
      ShowMessage('Fünfter');
  end;
end;

Für diesen Beitrag haben gedankt: probare
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mi 04.12.13 17:42 
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
... und wenn in deinen Strings ausschließlich "Zahlenwerte" stehen, wandelst du die Strings in einen Integer um, was dich in die Lage versetzt, doch noch einen Case-Block einzusetzen.


Stehen leider nicht nur Zahlenwerte drin.
Der jeweils 3. String ist ein Buchstabe, der jeweils 5. ist eine Zahl und das auch nur Blöckeweise - also Block wird mit '##X 123' geöffnet und mit '##Y 123' geschlossen.
Habe das Ganze jetzt erst einmal so gelöst, dass ich per if-Abfrage das 1. und das 3. Zeichen auslese, damit ich nur mit geöffneten Blöcken arbeite (stoppt sobald '##Y' ) auftaucht.
In der Bearbeitung verwende ich eine weitere Variable, so dass ich die Blockeinträge auslesen kann.
Ist der Block beendet, bin ich wieder in der for-Schleife.
Da ich ja nun weiß, dass die 2. Zeile für mich vermutlich uninteressant ist, arbeite ich mit einer 3. Variablen, die den Lesezähler künstlich hochschraubt.
Somit wird nicht jede Zeile der Textdatei durch alle if-Abfragen gejagd, sondern nur Blockanfang + Blockende, was die Rechenzeit wesentlich verbessert hat.

Das das hier sich natürlich etwas unverständlich liest, das Ganze einmal als Quellcode:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
...
s := 0;
  for i := 0 to ListBoxRead.Items.Count -1 do
    begin
    k := i+s; //schiebt die Schleife künstlich vor, um unnötige Abfragen zu überspringen
    if k >= ListBoxRead.Items.Count then break; //sorgt dafür, dass List out of bounds vermieden wird
    if ListBoxRead.Items[k][1] = '#' then
        if ListBoxRead.Items[k][3] = 'H' then
        begin
        s := 0//setzt den Hilfszähler zurück
      //2
        if ListBoxRead.Items[k] ='##X 2' then
          begin
            while ListBoxRead.Items[k+s] <> '##Y 2' do begin
              StringList2.Add(ListBoxRead.Items[k+s]);
              s := s+1;
            end;
            StringList2.Add('##Y 2');
            continue;
          end;
         ...
     end;
...



@WasWeißDennIch: Danke, werde mich damit mal näher beschäftigen müssen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 04.12.13 18:17 
Eine TStringList statt einer Listbox kann auch noch ein wenig mehr Geschwindigkeit bringen. Eine Listbox ist nur zur Anzeige da, nicht zur Verarbeitung. Du kannst es dem Benutzer natürlich dort zusätzlich anzeigen, aber verarbeiten würde ich es in einer TStringList.

Dazu kommt dann das Einlesen, das geht mit einer MMF sehr schnell, wenn die Datei größer ist.

Zum Vergleich:
Ich habe eine 350 MiB große Registrydatei in wenigen Sekunden komplett geparst und in einem Baum angezeigt. Und da muss ich jeden Eintrag entsprechend zerlegen und prüfen...

Für diesen Beitrag haben gedankt: probare
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 04.12.13 19:00 
Statt continue würde es wesentlich naheliegender auch ein else tun:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
if ... then
  begin
  end
else if ... then
  begin
  end
else if ...

Wenn du die Zweige dann noch nach "Häufigkeit" sortierst, dann bringt das auch nochmal was.

Für eine etwas ausgefeiltere Lösung wäre wohl eine Hashtabelle/-funktion geeignet. Du übersetzt deinen String in einen (weitestgehend eindeutigen) Integer und benutzt den dann für case (oder als Funktionspointer...). Das wird wohl die effizienteste Möglichkeit sein.
//Edit: ich nehme an, dass genau dies durch AnsiIndexText, wie oben erwähnt, realisiert wird

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)

Für diesen Beitrag haben gedankt: probare
Tranx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 648
Erhaltene Danke: 85

WIN 2000, WIN XP
D5 Prof
BeitragVerfasst: Mi 04.12.13 19:20 
Wenn Deine Strings in einer Stringlist stehen, kannst Du auch folgendes abfragen:


ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
//function IndexOf(const S: string): Integer; override;
//Beschreibung
//Mit IndexOf können Sie das erste Auftreten des Strings S ermitteln. IndexOf gibt den auf Null basierenden Index des Strings zurück. Wenn S also mit dem ersten String in
//der Liste übereinstimmt, liefert IndexOf 0 zurück, beim zweiten String 1 usw. Ist der String nicht vorhanden, gibt IndexOf -1 zurück.

  var
    StrgLst : TStringList;
    s : string;
  begin
    StrgLst := TStringList.Create;
    // hier die Stringliste StrgLst füllen
    
    //z.B.: s = '## 123':
    s := '## 123';
    n = StrgLst.IndexOf(s); 
    case n of 

    0 : ..;
    1 : ..;
    else
    ..;
    end;
  end;

_________________
Toleranz ist eine Grundvoraussetzung für das Leben.

Für diesen Beitrag haben gedankt: probare
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mi 04.12.13 20:41 
user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Deine Strings in einer Stringlist stehen, kannst Du auch folgendes abfragen:


ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
//function IndexOf(const S: string): Integer; override;
//Beschreibung
//Mit IndexOf können Sie das erste Auftreten des Strings S ermitteln. IndexOf gibt den auf Null basierenden Index des Strings zurück. Wenn S also mit dem ersten String in
//der Liste übereinstimmt, liefert IndexOf 0 zurück, beim zweiten String 1 usw. Ist der String nicht vorhanden, gibt IndexOf -1 zurück.

  var
    StrgLst : TStringList;
    s : string;
  begin
    StrgLst := TStringList.Create;
    // hier die Stringliste StrgLst füllen
    
    //z.B.: s = '## 123':
    s := '## 123';
    n = StrgLst.IndexOf(s); 
    case n of 

    0 : ..;
    1 : ..;
    else
    ..;
    end;
  end;


Das habe ich auch schon in Betracht gezogen. Da s aber ~600 verschiedene Varianten annehmen kann, hätte es denselben Effekt einer if-Abfrage, wenn ich mich nicht irre.



user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Eine TStringList statt einer Listbox kann auch noch ein wenig mehr Geschwindigkeit bringen. Eine Listbox ist nur zur Anzeige da, nicht zur Verarbeitung. Du kannst es dem Benutzer natürlich dort zusätzlich anzeigen, aber verarbeiten würde ich es in einer TStringList.

Dazu kommt dann das Einlesen, das geht mit einer MMF sehr schnell, wenn die Datei größer ist.

Zum Vergleich:
Ich habe eine 350 MiB große Registrydatei in wenigen Sekunden komplett geparst und in einem Baum angezeigt. Und da muss ich jeden Eintrag entsprechend zerlegen und prüfen...

Im Moment speicher ich jeden Block in seine zugehörige TStringList (es gibt hier ca 600 verschiedene), um diese einerseits zu sortieren, andererseits nur eine Ausgabe brauche.
Das Programm braucht zur Zeit ca 12 Sekunden für das Sortieren einer 1,6 mb großen Datei. Dabei ist die Prozessorauslastung recht hoch, was ich zu Minimieren versuche, soweit es denn geht.
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mi 04.12.13 23:29 
Die Prozessorauslastung kann egal sein - solange sie angenehm kurz ist ;)

Wie wäre es mit einem anderen Ansatz ?

Zuerst: Keine Listbox nehmen, die sind dafür nicht gedacht, der Overhead für die ganze visuelle Aufbereitung ist gewaltig. Eine TStringList ist wirklich besser geeignet, zur Visualisierung reicht ein Progressbar.

Als nächstes würde ich die Datei gar nicht mit LoadFromFile in die TSL einsaugen. Diese Art Listen haben das Problem, das sie mit wachsender Zeilenzahl immer langsamer werden, bis es irgendwann eine Minute dauert, um eine Zeile anzuhängen... Stattdessen würde ich die Datei zeilenweise lesen und dort schon mal schauen, was überhaupt relevant ist. Das Relevante geht in die StringList, die dann genauer untersucht wird. Beim Einfügen in die StringList kann man sich dann überlegen, ob man wirklich "###X 123" einfügen muß - oder man das gleich übersetzt in "04123", das "##Y 123" in "05123" etcpp... Die restlichen Ideen zur Verarbeitung überlasse ich Dir ;)

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.


Zuletzt bearbeitet von OlafSt am Mi 04.12.13 23:32, insgesamt 1-mal bearbeitet
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 378
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Mi 04.12.13 23:34 
Hallo Jaenicke,

das MMF liest sich gut, aber was bedeutet MMF? Weder in den Hilfen für Delphi und Windows noch mit Google habe ich was sinnvolles finden können.

Gruß
GuaAck

Für diesen Beitrag haben gedankt: probare
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 04.12.13 23:56 

Für diesen Beitrag haben gedankt: probare
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Mi 04.12.13 23:56 
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Die Prozessorauslastung kann egal sein - solange sie angenehm kurz ist ;)

Wie wäre es mit einem anderen Ansatz ?

Zuerst: Keine Listbox nehmen, die sind dafür nicht gedacht, der Overhead für die ganze visuelle Aufbereitung ist gewaltig. Eine TStringList ist wirklich besser geeignet, zur Visualisierung reicht ein Progressbar.

Als nächstes würde ich die Datei gar nicht mit LoadFromFile in die TSL einsaugen. Diese Art Listen haben das Problem, das sie mit wachsender Zeilenzahl immer langsamer werden, bis es irgendwann eine Minute dauert, um eine Zeile anzuhängen... Stattdessen würde ich die Datei zeilenweise lesen und dort schon mal schauen, was überhaupt relevant ist. Das Relevante geht in die StringList, die dann genauer untersucht wird. Beim Einfügen in die StringList kann man sich dann überlegen, ob man wirklich "###X 123" einfügen muß - oder man das gleich übersetzt in "04123", das "##Y 123" in "05123" etcpp... Die restlichen Ideen zur Verarbeitung überlasse ich Dir ;)


Der Ansatz gefällt mir.
Werde mir morgen mal näher dazu Gedanken machen.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 05.12.13 07:34 
Hallo,

ist das Problem, ohne Platzhalter freilich, in www.entwickler-ecke....iewtopic.php?t=83336 mittels "wu-manger" angegangen worden?

Gruß Horst

Für diesen Beitrag haben gedankt: probare
mandras
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 432
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: Do 05.12.13 12:35 
Du kannst für das Suchen eine StringList mit sorted=true einsetzen,
dann erfolgt bei indexof oder find eine binäre suche die recht flott ist.

Für diesen Beitrag haben gedankt: probare
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Do 05.12.13 23:21 
user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,

ist das Problem, ohne Platzhalter freilich, in www.entwickler-ecke....iewtopic.php?t=83336 mittels "wu-manger" angegangen worden?

Gruß Horst


Noch nicht, sind auch schon eine ganze Menge Ideen und Ansätze auf einmal, was ich so auf Anhieb nicht erwartet hätte. Werde am Wochenende mal einige Varianten ausprobieren und testen.

Herzlichen Dank
probare Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Fr 06.12.13 14:22 
user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
Du kannst für das Suchen eine StringList mit sorted=true einsetzen,
dann erfolgt bei indexof oder find eine binäre suche die recht flott ist.


wie gesagt, stehen in der textdatei blöckeweise informationen.
setze ich nun sorted = true, werden die blöcke durcheinander geworfen.

trotzdem danke, die information könnte mir in anderen projekten evtll noch behilflich sein.