Autor |
Beitrag |
probare
Hält's aus hier
Beiträge: 7
|
Verfasst: 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:
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:
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 '## 123' : begin ... end;
'## 234' : begin ... end; end; |
Wie funktioniert es richtig?
Moderiert von Martok: Quote- durch Delphi-Tags ersetzt
|
|
baumina
      
Beiträge: 305
Erhaltene Danke: 61
Win 7
Delphi 10.2 Tokyo Enterprise
|
Verfasst: 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.
Delphi-Quelltext 1: 2: 3: 4: 5:
| if ListBoxRead.Items[i] := '## 123' begin ... continue; end; |
Für diesen Beitrag haben gedankt: probare
|
|
probare 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.12.13 15:57
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
|
Verfasst: 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
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Mi 04.12.13 17:33
Mit AnsiIndexText ist man ja auch in der Lage, case zu verwenden:
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..4] of 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 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.12.13 17:42
Perlsau hat folgendes geschrieben : | ... 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:
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; if k >= ListBoxRead.Items.Count then break; if ListBoxRead.Items[k][1] = '#' then if ListBoxRead.Items[k][3] = 'H' then begin s := 0; 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
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
      

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)
|
Verfasst: Mi 04.12.13 19:00
Statt continue würde es wesentlich naheliegender auch ein else tun:
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
      
Beiträge: 648
Erhaltene Danke: 85
WIN 2000, WIN XP
D5 Prof
|
Verfasst: Mi 04.12.13 19:20
Wenn Deine Strings in einer Stringlist stehen, kannst Du auch folgendes abfragen:
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:
| var StrgLst : TStringList; s : string; begin StrgLst := TStringList.Create; 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 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.12.13 20:41
Tranx hat folgendes geschrieben : | Wenn Deine Strings in einer Stringlist stehen, kannst Du auch folgendes abfragen:
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:
| var StrgLst : TStringList; s : string; begin StrgLst := TStringList.Create; 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.
jaenicke hat folgendes geschrieben : | 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
      
Beiträge: 486
Erhaltene Danke: 99
Win7, Win81, Win10
Tokyo, VS2017
|
Verfasst: 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
      
Beiträge: 378
Erhaltene Danke: 32
Windows 8.1
Delphi 10.4 Comm. Edition
|
Verfasst: 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
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 04.12.13 23:56
Für diesen Beitrag haben gedankt: probare
|
|
probare 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.12.13 23:56
OlafSt hat folgendes geschrieben : | 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
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: 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
      
Beiträge: 432
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: 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 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 05.12.13 23:21
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 
Hält's aus hier
Beiträge: 7
|
Verfasst: Fr 06.12.13 14:22
mandras hat folgendes geschrieben : | 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.
|
|