Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Suche effizienter machen (https://www.delphipraxis.net/120958-suche-effizienter-machen.html)

Zioone 19. Sep 2008 13:11


Suche effizienter machen
 
Hallo,
mein Programm soll für voreingestellte Schlüsselwörter, wenn etwas gesucht oder die Datei geöffnet wurde, farbig hervorheben.

Das funktioniert auch.
Mein Problem ist nur, wenn ich sehr große Dateien habe ala Zeilen >2000, dann dauert das eine halbe Ewigkeit.

Ich wollte nur mal wissen ob wer eine Idee hat, dieses Suchen/markieren effizienter zu machen oder ob ich mit der Geschwindigkeit leben muss.
Hier der Code:
Delphi-Quellcode:
i2:=0;

    for ii := 0 to FSelectCount-1 do
    begin
      if FSelect.Strings[ii] <>'' then
      begin
        if (text<>'') OR (text2<>'') OR (text3<>'') then
        begin
          if (Pos( LowerCase(text), LowerCase( FSelect.Strings[ii] ) )) > 0 then
          begin
            i:=Pos( LowerCase(text), LowerCase( FSelect.Strings[ii] ) );
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text);
            mmoLines.SelAttributes.Color := c;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
          if Pos( LowerCase(text2), LowerCase( FSelect.Strings[ii] ) ) > 0 then
          begin
            i:=Pos( LowerCase(text2), LowerCase( FSelect.Strings[ii] ) );
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text2);
            mmoLines.SelAttributes.Color := c2;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
          if Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) ) > 0 then
          begin
            i:=Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) );
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text3);
            mmoLines.SelAttributes.Color := c3;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
        end;
      end;
        i2 := i2+Length(FSelect.Strings[ii])+2;
    end;
wobei FSelect eine Stringlist ist
und text und color und c aus einer ini datei gelesen werden

über Ideen wäre ich dankbar :)

taaktaak 19. Sep 2008 13:29

Re: Suche effektiver machen
 
Moin, Moin,
Spontan drei Bemerkungen dazu:
1) Das ständige "LowerCase(FSelect.Strings[ii])" kostet Zeit und würde überflüssig, wenn es 1x VOR der Schleife erledigt wird.
2) Prüfe, ob die Suche nach einem Schlüsselwort in der Stringlliste mit IndexOf() schneller ist als mit Pos()
3) Wenn dein Code seinen Zweck erfüllt, ist er bereits effektiv! Wenn er seinen Zweck erfüllen und besonders schnell sein soll, dann ist er effizient!

nahpets 19. Sep 2008 13:35

Re: Suche effektiver machen
 
Hallo,

mal so ein bisserl schmutzig von mir dahingeschrieben:

Delphi-Quellcode:
if (text<>'') OR (text2<>'') OR (text3<>'') then
Mach Dir dafür 'nen boolschen Schalter, dann musst Du nur einmal abfragen

Delphi-Quellcode:
b := (text<>'') OR (text2<>'') OR (text3<>'');
Das kannst Du schon vor Deiner For-Schleife machen, ist b = false, kannst Du Dir die For-Schleife direkt sparen.

Text... werden bei jeder Abfrage mit Lowercase behandelt, mache das einmal vor der Schleife.
Delphi-Quellcode:
if Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) ) > 0 then
begin
  i:=Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) );
Prüfe bitte, ob Du vorab auch FSelect.Strings[ii] in Lowercase wandeln kannst, dann mach das vorab in einer separaten Schleife.

Die Position wird hier zweimal ermittelt, eventuell machst Du das besser so:
Delphi-Quellcode:
i := Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) );
if i > 0 then
begin
@taaktaak, IndexOf ist hier nicht geeignet, da nicht nach Strings in der Liste gesucht wird, sondern nach Teilstrings in den einzelnen Listeneinträgen, das kann IndexOf meines Wissens nicht.

Stephan

PS: taaktaak war schneller als ich :x

DerDan 19. Sep 2008 13:38

Re: Suche effektiver machen
 
Hallo,


wenn du die Einfärbung nur brauchst um sie auf dem Bildschirm darzustellen dann ist es effizienter wenn du nur die Zeilen bearbeitest, die momentan sichtbar sind.

Dafür gibt es mit der SynEdit auch schon fertige Komponenten.


mfg DerDan

Zioone 19. Sep 2008 13:55

Re: Suche effizienter machen
 
danke schon mal für die Antworten :thumb:

Nur was ich nicht ganz verstehe ist das ich das LowerCase vor der Schleife machen soll.
Sann kennt er doch gar nicht "ii"?!
und wenn ich das wieder in einer Schleife mache, hab ich ja auch nichts mit gewonnen o_O

Sorry, bin Anfänger ^^'

taaktaak 19. Sep 2008 14:00

Re: Suche effizienter machen
 
Mir ist nicht klar, wie du die Schlüsselwörter bereitstellst.
Lege eine Stringlist an, in der jedes Schlüsselwort ein Listenelement ist und dort als LowerCase abgelegt ist. Dann kannst du auch mit IndexOf() arbeiten.

Zioone 19. Sep 2008 14:04

Re: Suche effizienter machen
 
Die Schlüsselwörter speicher ich vorher in eine IniDatei
und lese sie dann an der Stelle wieder aus
Delphi-Quellcode:
ini :=TIniFile.Create(ExtractFilePath(ParamStr(0))+'settings.ini');
 
    text := ini.ReadString('settings','text1','Error');
    color := ini.ReadString('settings','color1','clred');
    if color<>'' then
    begin
      c:=StringToColor(color);
    end;
jetzt nur mal für den ersten text

nahpets 19. Sep 2008 14:08

Re: Suche effizienter machen
 
Hallo,
Zitat:

Zitat von Zioone
danke schon mal für die Antworten :thumb:

Nur was ich nicht ganz verstehe ist das ich das LowerCase vor der Schleife machen soll.
Sann kennt er doch gar nicht "ii"?!
und wenn ich das wieder in einer Schleife mache, hab ich ja auch nichts mit gewonnen o_O

Sorry, bin Anfänger ^^'

Ich versuchs mal:

Für jeden Eintrag in FSelect.Strings werden text, text2 und text3 in Lowercase gesetzt. Das kannst Du vor der Schleife machen, diese Werte sind unabhängig vom Inhalt der Strings.

Wenn Du ohne Probleme FSelect.Strings in Kleinbuchstaben umwandeln kannst, weil es ausserhalb der Schleife z. B. nicht gebraucht wird, dann mach' eine zweite Schleife vor die jetzige Schleife.

Delphi-Quellcode:
for ii := 0 to FSelect.count - 1 ) do FSelect.Strings[ii] := lowercase(FSelect.Strings[ii]);
dann wird aus
Delphi-Quellcode:
if Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) ) > 0 then
begin
  i:=Pos( LowerCase(text3), LowerCase( FSelect.Strings[ii] ) );
Delphi-Quellcode:
if Pos(text3,FSelect.Strings[ii]) > 0 then
begin
  i:=Pos(text3,FSelect.Strings[ii]);
und wenn Du das dann noch vertauschst, wird daraus
Delphi-Quellcode:
i:=Pos(text3,FSelect.Strings[ii]);
if i > 0 then
begin
Dadurch sparst Du Dir sehr viele Lowercase und sehr viele Pos.

Stephan

Zioone 19. Sep 2008 14:21

Re: Suche effizienter machen
 
ah, ok jetzt hab ich das verstanden.
Danke

Nur ob ich die Strings[ii] in einer seperaten schleife klein mache oder in der "richtigen" ist doch eigentlich egal?! :gruebel:

Und wie könnt ich jetzt mal am besten testen (in Zeitwerten) wie effizienter das ganze geworden ist?
So rein nach Gefühl ist das denke schon bisschen schneller, aber würde mich mal interessieren wie viel das nun genau gebracht hat.
Muss ich dafür jetzt testweise kompletten Timer programmieren?

nahpets 19. Sep 2008 14:43

Re: Suche effizienter machen
 
Hallo,
Zitat:

Zitat von Zioone
ah, ok jetzt hab ich das verstanden.
Danke

Nur ob ich die Strings[ii] in einer seperaten schleife klein mache oder in der "richtigen" ist doch eigentlich egal?! :gruebel:

Und wie könnt ich jetzt mal am besten testen (in Zeitwerten) wie effizienter das ganze geworden ist?
So rein nach Gefühl ist das denke schon bisschen schneller, aber würde mich mal interessieren wie viel das nun genau gebracht hat.
Muss ich dafür jetzt testweise kompletten Timer programmieren?

Nein, in der separaten Schleife machst Du alles nur einmal kein, in der großen Schleife jedes mal, für Text, für text2 und text3.

Wenn die Liste also 1000 Einträge enthält, wird jeder Eintrag der Stringliste 1 mal kleingemacht.
In Deiner Variante wird jeder Eintrag der Stringliste 1000 mal drei mal kleingemacht. Das läppert sich zusammen, auch wenn einmal kleinmachen allein gaaaanz schnell geht :wink:

Was das bringt: Nimm Deine "alte" Fassung mit einer definierten Liste und definiertem Text und schau auf die Uhr, wielange das dauert. Dann überarbeitest Du das Programm und machst den gleichen Test nochmal.

Die Uhr kannst Du Dir mit Delphi "selber bauen".

Vorher:
Delphi-Quellcode:
ShowMessage(DateTimeToStr(Now));
Uhrzeit aufschreiben
Nachher:
Delphi-Quellcode:
ShowMessage(DateTimeToStr(Now));
Uhrzeit aufschreiben und Zeitdifferenz ausrechnen.

Stephan

jfheins 19. Sep 2008 15:00

Re: Suche effizienter machen
 
Zitat:

Zitat von nahpets
Die Uhr kannst Du Dir mit Delphi "selber bauen".

Vorher:
Delphi-Quellcode:
ShowMessage(DateTimeToStr(Now));
Uhrzeit aufschreiben
Nachher:
Delphi-Quellcode:
ShowMessage(DateTimeToStr(Now));
Uhrzeit aufschreiben und Zeitdifferenz ausrechnen.

Stephan

Diese Methode eigent sich besonders gut - allerdings weniger um die Zeit des Verfahrens zu messen, als vielmehr die Schreibgeschwindigkeit :mrgreen:

(Während des showmessages() ist das Programm angehalten. Du stoppst also die Zeit, die du zum schreiben brauchst + Zeit der Funktion)

Besser ist es natürlich, die Zeit in einer Variablen zu speichern und nachher die different zu nehemen. Siehe hierzu auch GetTickCount - das ist dann direkt in Millisekunden ;)

nahpets 19. Sep 2008 15:19

Re: Suche effizienter machen
 
Hallo jfheins,

Du hast natürlich recht, wenn es Dir um sehr präzise Zeitmessungen geht, aber wenn ich eine Schleife über ein paar tausend Zeilen mache und feststelle, dass dauert 10 Minuten und beim nächsten Mal, nachdem ich das Programm geändert habe, dauert es fünf Minuten, dann reicht meine "manuelle" Stopuhr, gehe hier in dem Beispiel nicht davon aus, dass wir hier irgendwas hochperformantes bauen wollen. Klar könnte man vor der Schleife die Zeit oder Tickcount auf ein Label schreiben, nachher auf ein Anderes und auf ein Drittes die Differenz.

Wollte mit komplizierten Beispielen neben dem eigentlichen Problem nicht noch 'nen "Fragenkatalog" aufmachen. :wink:

Stephan

alzaimar 19. Sep 2008 15:35

Re: Suche effizienter machen
 
Wörter sucht man am schnellsten mit einem Dawg oder einer Hashmap ('TStringDictionary'). Such mal hier danach...

jfheins 19. Sep 2008 15:44

Re: Suche effizienter machen
 
Zitat:

Zitat von nahpets
Hwenn ich eine Schleife über ein paar tausend Zeilen mache und feststelle, dass dauert 10 Minuten und beim nächsten Mal, nachdem ich das Programm geändert habe, dauert es fünf Minuten, dann reicht meine "manuelle" Stopuhr

Das mag sein - aber wenn das hervorheben vön Wörtern in einem Textfeld länger dauert als eine Sekunde, dann läuft da irgendetwas gehörig schief ;)

@Topic:

Hast du mal versucht, den Code zwischen BeginUpdate() und EndUpdate() zu platzieren?
Das könnte auch noch einmal schneller sein ;)

Zioone 21. Sep 2008 21:37

Re: Suche effizienter machen
 
Hi,
und sorry das ich mich jetzt erst wieder melde^^.

Da ich Anfänger bin hab ich leider kein Plan was BeginUpdate() und EndUpdate(). Wo kann ich da Code plazieren oder wie funktioniert das? Aus Internetsuche bin ich da leider auch nicht sonderlich schlau geworden.

ebenso wenig aus TStringDictionary. So ganz schlau bin ich da auch nicht draus geworden. Wäre schön wenn da jemand mir noch mal helfen könnte^^

Zioone 23. Sep 2008 09:49

Re: Suche effizienter machen
 
ok, noch mal anderes gefragt
hab noch mal gesucht und bissel was gefunden, wegen dem beginupdate und endupdate.
das ganze bringt jetzt, wenn überhaupt eine sekunde gewinn. Hab ich das an der falschen stelle oder falsch verstanden?
Delphi-Quellcode:
 b := (text<>'') OR (text2<>'') OR (text3<>'');

    if b=true then
    begin
      text:=LowerCase(text);
      text2:=LowerCase(text2);
      text3:=LowerCase(text3);

      mmoLines.Lines.BeginUpdate;
      for ii := 0 to FSelect.Count-1 do
      begin
        if FSelect.Strings[ii] <>'' then
        begin
          i := Pos( text, LowerCase(FSelect.Strings[ii]) );
          if i > 0 then
          begin
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text);
            mmoLines.SelAttributes.Color := c;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
          i := Pos( text2, LowerCase(FSelect.Strings[ii]) );
          if i > 0 then
          begin
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text2);
            mmoLines.SelAttributes.Color := c2;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
          i := Pos( text3, LowerCase(FSelect.Strings[ii]) );
          if i > 0 then
          begin
            mmoLines.SelStart := i+i2-1;
            mmoLines.SelLength := Length(text3);
            mmoLines.SelAttributes.Color := c3;
            mmoLInes.SelAttributes.Style := [fsBold];
            mmoLines.SelLength := 0;
          end;
        end;
        i2 := i2+Length(FSelect.Strings[ii])+2;
      end;
      mmoLines.Lines.EndUpdate;
    end;

taaktaak 23. Sep 2008 10:00

Re: Suche effizienter machen
 
Was ich immer noch nicht verstehe:
Warum ziehst du das mehrfache
Delphi-Quellcode:
LowerCase(FSelect.Strings[ii])
nicht vor die Schleife?
Und das "if b=true then.." sollte man auch nicht tun (vorher wars besser)

Zioone 23. Sep 2008 10:04

Re: Suche effizienter machen
 
hi,
1. weil es eh kein gewinn gebracht hat an Zeit
2. wenn ich es einmal davor mache, hat er meinen Text komplett klein gemacht (logisch) nur wenn ich dann was gesucht habe, war der Text auch komplett klein, was mir nichts bringt. Und dann bringt mir mein "groß/kleinschreibung beachten - button" nichts mehr *gg*
aber wie gesagt es hat, bei einer ungefähr 500kb großen datei mit ca. 6800 Zeilen, eh nichts gebracht.

und wieso war das b=true vorher besser?

Tyrael Y. 23. Sep 2008 10:08

Re: Suche effizienter machen
 
Für die Geschwindigkeitmessung würde ich folgendermassen vorgehen.

Delphi-Quellcode:
var LBegin,
    LEnd: integer;

    LTime: String;
begin
  LBegin := GetTickCount();
  try
    HierMacheIchDieArbeit();
  finally
    LEnd := GetTickCount;
   
    EinLabel.Caption := IntToStr(LEnd - LBegin);
   
  end;
end;

Zioone 23. Sep 2008 10:22

Re: Suche effizienter machen
 
sind das millisekunden?

also auf alle Fälle mal alles durchprobiert:
30282 mit beginupdate/endupdate ohne extra schleife für Lowercase
30578 ohne beginupdate/endupdate ohne extra schleife für Lowercase
30219 mit beginupdate/endupdate mit extra schleife für Lowercase
30906 ohne beginupdate/endupdate mit extra schleife für Lowercase

taaktaak 23. Sep 2008 10:27

Re: Suche effizienter machen
 
Na, der Vorschlag von "nahpets" mit der Zusammenfassung ist nur sinnvoll, wenn diese Prüfung innerhalb der Schleife immer wieder durchlaufen würde. Nun steht sie aber vor der Schleife. dann ist das nicht mehr notwendig. MAn sollte nicht auf =true prüfen. Begründungen gibt's hier im Forum an verscheidener Stelle.

Muss zugeben, das ich deinen Schnipsel immer weniger verstehe. Du hast 3 Edits, eine Stringliste und ein Memo. Die Worte, die in den Edits und der Stringliste enthalten sind, sollen im Memo fett markiert werden? Du vergleichst Edits/Liste mit LowerCase, dennoch dürfen die Listeneinträge nicht in LowerCase verändert werden.

Sorry, da blick ich nicht durch...

// edit: Ja, Millisekunden. Schau mal in die Win-API Hilfe.

Tyrael Y. 23. Sep 2008 10:27

Re: Suche effizienter machen
 
Ja es sind Millisekunden.

Wenn du jede Version nur einmal durchführst ist das nicht wirklich signifikant.

Delphi-Quellcode:
const C_COUNT = 100;
var LBegin,
    LEnd, i: integer;

    LTime: String;
begin
  LBegin := GetTickCount();
  try
    for i := 0 to C_COUNT do
    begin
      HierMacheIchDieArbeit();
    end;
  finally
    LEnd := GetTickCount;
   
    EinLabel.Caption := IntToStr(LEnd - LBegin);
   
  end;
end;
So wird die Arbeit jetzt 100 mal gemacht.
Umso öfter es möglich ist in annehmbarer Zeit umso öfter solltest du die Schleife laufen lassen.
Wichtig wäre auch, daß du das Ganze startest und bis zum Ende den Rechner in Ruhe lässt.
Aktionen nebenbei kosten Prozesserzeit.

Zioone 23. Sep 2008 10:37

Re: Suche effizienter machen
 
Zitat:

Zitat von taaktaak
Muss zugeben, das ich deinen Schnipsel immer weniger verstehe. Du hast 3 Edits, eine Stringliste und ein Memo. Die Worte, die in den Edits und der Stringliste enthalten sind, sollen im Edit fett markiert werden? Du vergleichst Edits/Liste mit LowerCase, dennoch dürfen die Listeneinträge nicht in LowerCase verändert werden.

ich habe 2 Stringlisten und ein Richedit, und die 3texte kommen aus einer inidatei die ich vorher über naja Farbkomponente einlese und in der ini datei speicher.
ich speicher den gesamten text in die Stringliste, wenn auf Suchbutton geklickt wurde, dann noch in die andere Stringliste, um mit der danach mit einer suche dort zu suchen dort ändert sich der Text, logischerweise. Aber der orginaltext darf sich ja nicht ändern.
Jedenfalls wird dann bei der suche der komplette text klein angezeigt.
~.~ Text doof geschrieben aber der hat damit ja auch eigentlich nichts zutun.

EDIT: um genau zu sein sind es 3 Stringlisten
Orignaltext
Zwischenspeicher
Suchergebnis

@Tyrael Y.
kanns ja dann noch mal machen, aber ob nu 1 sekunden hin oder her. Gibt es nichts was paar Sek mehr bringen kann?

Tyrael Y. 23. Sep 2008 10:41

Re: Suche effizienter machen
 
Zitat:

Zitat von Zioone
@Tyrael Y.
kanns ja dann noch mal machen, aber ob nu 1 sekunden hin oder her. Gibt es nichts was paar Sek mehr bringen kann?

Davon wirst du nix haben, da die Unterschiede dann im wahrscheinlich im Zehntelsekundenbereich liegen werden.
Machst du es mit einer Schleife und lässt es sehr oft laufen, werden sich die Unterschiede aufaddieren und du wirst Unterschiede von mehreren Sekunden/Minuten sehen, je nachdem wie lang die Schleife läuft.

taaktaak 23. Sep 2008 10:58

Re: Suche effizienter machen
 
Bin heute wirklich etwas begriffsstutzig:
Du hast also einen Text in einem RichEdit und willst in diesem Text 3 Worte unter Berücksichtigung der Groß-/Kleinschreiben suchen und, wenn gefunden, fett markieren?

Zioone 23. Sep 2008 11:03

Re: Suche effizienter machen
 
taaktaak:
das doch total irrelevant dafür ^^'. Ich lade den kompletten inhalt in FSelect rein, bzw auch in mein Richedit, dort möchte ich egal wegen groß/kleinschreibung, die wörter markieren.

Dann gibt es noch eine Suche, die dann den kompletten text klein ausgibt, wenn ich das dort über die schleife klein mache.

@Tyrael Y.
ja, das mein ich ja das die unterschiede nur im zentelbereich sind und mir eh nichts groß bringen. Nur versteh nicht, was ich machen kann...

jfheins 23. Sep 2008 11:06

Re: Suche effizienter machen
 
Ich denke nicht, ,dass das Verständnis deines Code irrelevant ist ;)

Also du hast einen Text? Und den lädst du in eine Stringliste und ein Richedit? Und dann hast du 3 Wörter (können das noch mehr werden?) die du hervorheben möchtest??

taaktaak 23. Sep 2008 11:10

Re: Suche effizienter machen
 
Ich halte eine exakte Aufgabendefinition keineswegs für irrelevant, es ist vielmehr eine unabdingbare Voraussetzung, dich bei der Problemlösung unterstützen zu können. Aus dem Codeschnipsel und dem bisher geschriebenen gehen die eigentlichen Anforderungen nicht zweifelsfrei hervor.

Aber vielleicht sind andere Forumsmitglieder ja intuitiver.
Ich geb's auf...
:roll:

Zioone 23. Sep 2008 11:16

Re: Suche effizienter machen
 
ja, aber die suche hat ja primär nichts mit dem textmakieren zutun.
also:
-datei öffnen
-text in Stringlist laden und Richedit
-in Prozedur Text markieren gehen
-suchwörter aus ini datei laden (zur zeit höchstens 3 wörter, vllt später mal mehr dynamisch aber erst mal soll das reichen)
-suche + markieren starten wie schon quelltext hier geschrieben
-ende fürs öffnen

jfheins 23. Sep 2008 11:34

Re: Suche effizienter machen
 
Gut, ich skizziere mal, wie ich das machen würde:

Text aus Datei in StringList & Richedit laden: Hast du offenbar schon.

StringList mit Wörtern laden.
Delphi-Quellcode:
uses strutils;

var
  i, position: integer;
Wörter: TStringlist;
text: String;

Richedit.Lines.Beginupdate();
for i = 0 to Wörter.Count do
begin
  position = pos(Wörter[i], text);
  while(position != 0)
  begin
    // Highlighting mit positioin und length(wörter[i])
    position = posex(Wörter[i], text, position);
  end;
end;
Richedit.Lines.EndUpdate();
Der Cde geht also nicht den Text durch, um zu ürüfen, ob da wörter sind, sondern geht die wörter durch und prüft ob sie im Text sind.

Wg. Case-Sensitiveness: da gibt es evtl. noch andere pos-Funktionen, bei denen man das einstellen kann ;)

alzaimar 23. Sep 2008 11:59

Re: Suche effizienter machen
 
Das kann man noch einkürzen:
Delphi-Quellcode:
Procedure TForm1.HighlightText;
Var
  ii,i2 : Integer;
  sLwcLine : String;

  Procedure _HighlightWord (Const aWord : String; aColor : TColor);
  Var
    p : Integer;

  Begin
    If aWord='' Then Exit;
    p := Pos (aWord, sLwcLine);
    If p>0 Then Begin
      mmoLines.SelStart := p + i2 - 1;
      mmoLines.SelLength := Length(aWord);
      mmoLines.SelAttributes.Color := aColor ;
      mmoLInes.SelAttributes.Style := [fsBold];
      mmoLines.SelLength := 0;
   End;
  End;

Begin
  if (text<>'') OR (text2<>'') OR (text3<>'') then begin
    i2 := 0; // Variable i2 muss initialisiert werden !
    text := LowerCase(text);
    text2 := LowerCase(text2);
    text3 := LowerCase(text3);
    mmoLines.Lines.BeginUpdate;
    Try
      for ii := 0 to FSelect.Count-1 do begin
        sLwcLine := LowerCase(FSelect[ii]);
        If sLwcLine<>'' Then Begin
          _HighLightWord (text, c);
          _HighLightWord (text2, c2);
          _HighLightWord (text3, c3);
        End;
        i2 := i2 + Length(FSelect.Strings[ii]) + 2;
      End;
    Finally
      mmoLines.Lines.EndUpdate;
    End
  End;
End;
Das 'highlighten' habe ich in eine lokale Prozedure ausgelagert, das Begin/Endupdate in Resourcenschutzblöcke gepackt, das LowerCase(FSelect[ii]) herausgezogen und die Variable 'i2' initialisiert (wieso heißt 'i2' eigentlich 'i2' und nicht z.B. 'iLineIndex'?).

Jetzt ist es etwas übersichtlicher.

@jfHeins: Bei deiner Lösung ist der große Pferdefuß die Generierung des 'Textes'. Ich vermute, Du meinst 'MyRichEdit.Lines.Text'. Das ist Speicher- und Zeitverschwendung, denn der 'Text' wird aus den Zeilen durch Stringkonkatenation gebildet. Ich denke, der Code ist schon ganz ok, die POS-Funktion könnte man noch durch eine schnellere Variante ersetzen (z.B. eine von FastCode oder FastStrings). Wenn man den Textpuffer direkt ansprechen könnte, wäre deine Variante aber sicherlich viel performanter.

guidok 23. Sep 2008 12:39

Re: Suche effizienter machen
 
Hier ist meine Variante:

Delphi-Quellcode:
var SuchPos: Integer;
begin
  if OpenDialog.Execute then
  begin
    if Length(edSuchwort.Text) > 0 then
    begin
      RichEdit.Lines.LoadFromFile(OpenDialog.FileName);
      SuchPos := 0;
      repeat
        SuchPos := RichEdit.FindText(edSuchwort.Text,SuchPos,Length(RichEdit.Lines.Text),[stWholeWord]);
        if SuchPos > -1 then
        begin
          RichEdit.SelStart := SuchPos;
          RichEdit.SelLength := Length(edSuchwort.Text);
          RichEdit.SelAttributes.Color := clRed;
          SuchPos := SuchPos + Length(edSuchwort.Text);
        end;
      until SuchPos = -1;
    end;
  end;
end;
Ich habe keinen Vergleich, wie schnell sie ist, aber sie funktioniert, wenn auch nur mit einem Suchwort. Für weitere Wörter muss man halt erweitern. Ein paar Kleinigkeiten könnte man auch noch optimieren, z.B. SuchLaenge := Length(edSuchwort.Text); im Voraus zuweisen.

EDIT

Der Aufruf von Length scheint viel Zeit zu benötigen. Ich habe das jetzt noch einmal so probiert und der Geschwindigkeitsunterschied ist immens.

Delphi-Quellcode:
var SuchPos, SuchLaenge, RichEditLaenge: Integer;
    STime, ETime: Integer;
begin
  if OpenDialog.Execute then
  begin
    SuchLaenge := Length(edSuchwort.Text);
    if SuchLaenge > 0 then
    begin
      RichEdit.Lines.LoadFromFile(OpenDialog.FileName);
      RichEditLaenge := Length(RichEdit.Lines.Text);

      STime := GetTickCount();

      SuchPos := 0;
      repeat
        SuchPos := RichEdit.FindText(edSuchwort.Text,SuchPos,RichEditLaenge,[stWholeWord]);
        if SuchPos > -1 then
        begin
          RichEdit.SelStart := SuchPos;
          RichEdit.SelLength := SuchLaenge;
          RichEdit.SelAttributes.Color := clRed;
          SuchPos := SuchPos + SuchLaenge;
        end;
      until SuchPos = -1;

      ETime := GetTickCount();
      lbZeit.Caption := IntToStr(ETime - STime);
      lbZeit.Visible := True;

    end;
  end;
end;

p80286 23. Sep 2008 13:27

Re: Suche effizienter machen
 
Hallo zusammen,

Warum ersetzt Ihr pos/posex/findtext nicht durch
Delphi-Quellcode:
Search_BMH_Unrolled
oder einen anderen Boyer_Moore-Abkömling? Die sind wirklich schnell!

Der Lösungsvorschlag aus #13 (Hashmap) scheint mir vor allem interessant, wenn mehrere Worte gesucht werden sollen.

Gruß
K-H

P.S. ich hab einmal Pos und Search_BMH_Unrolled gegeneinander laufen lassen und das Laufzeitverhältnis (50MB Text) war ungefähr 5:1

alzaimar 23. Sep 2008 13:34

Re: Suche effizienter machen
 
Zitat:

Zitat von alzaimar
...die POS-Funktion könnte man noch durch eine schnellere Variante ersetzen (z.B. eine von FastCode oder FastStrings).

Zitat:

Zitat von p80286
Warum ersetzt Ihr pos/posex/findtext nicht durch...

*trommel* Auch das Kleingedruckte lesen :zwinker:

Boyer-Moore ist nicht automatisch schneller, denn es muss zunächst der Suchtext analysiert werden, das erzeugt einen gewissen Overhead. Man müsste für jeden Suchtext eine eigene BM-Engine erzeugen, dann wäre das schneller. Allerdings dürften hier andere Kandidaten (QuickSearch, HorSpool) noch besser sein. In der (FastStrings-Unit ist ein vereinfachter BM (QS, denke ich) implementiert.

Boyer-Moore eignet sich zudem erst für längere Suchtexte, der Overhead ist einfach zu groß.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:07 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz