Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Warum ist das Löschen einer Zeile aus einer Memo so langsam? (https://www.delphipraxis.net/173945-warum-ist-das-loeschen-einer-zeile-aus-einer-memo-so-langsam.html)

Der schöne Günther 25. Mär 2013 16:33

Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Hallo-

Ich wundere mich:
  • 500 Zeilen in meiner Memo
  • Ich möchte nun die ersten 400 löschen
  • Das Löschen mittels 400x
    Delphi-Quellcode:
    Lines.Delete(index)
    dauert fast eine geschlagene Sekunde!!

Gibt es keine bessere Methode, die ersten x Strings aus einem
Delphi-Quellcode:
TStrings
-Objekt zu entfernen?


Entferne ich die ungewollten Zeilen folgendermaßen ist die Sache blitzschnell erledigt:
Delphi-Quellcode:
with someMemo do
   if Lines.Count > logMemoMaxLines then begin
      Lines.BeginUpdate();
      SelStart := 0;
      // Quelle: http://borland.newsgroups.archived.at/public.delphi.rtl.win32/200609/0609161352.html
      SelLength := Perform(EM_LINEINDEX, logMemoMaxLinesClear, 0);
      SelText := '';
      Lines.EndUpdate();
   end;
Warum ist das so?

BUG 25. Mär 2013 16:49

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Afaik ist Lines von TMemo keine TStringList. Anstelle dessen werden jedes mal Nachrichten verschickt.

Namenloser 25. Mär 2013 17:15

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Warum genau das so ist, weiß ich auch nicht, aber ich kann aus Erfahrung sagen, dass TMemo z.B. auch extrem langsam wird, wenn man sehr lange Texte dort reinlädt.

Vielleicht liegt es daran, dass Memo für Windows das gleiche ist wie ein Edit... d.h. der Inhalt ist eigentlich ein einziger String und es ist daher nicht wirklich zeilenorientiert. Wahrscheinlich muss Windows beim Memo den ganzen String ständig immer wieder von vorne durchscannen, um die Zeilenvorschübe zu finden. Ist aber reine Mutmaßung meinerseits.

Besser bedient ist man mit einem TRichEdit, bei dem die Eigenschaft Plain auf True gesetzt ist.

Der schöne Günther 25. Mär 2013 17:19

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Ich war bislang sehr zufrieden (auch bei mehreren tausend Zeilen), nur das Löschen ist katastrophal.

Bernhard Geyer 25. Mär 2013 17:48

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1208760)
Warum ist das so?

Weil du den Control mitteilst das anhängende Aktionen nach einem Ändern der Datenbasis erst mit dem EndUpdate erfolgen sollen.
und mit Begin/Endupdate wird statt 400* die aktion nur einmal aufgeführt. WEnn nun jedes Aktualisieren 0,25 ms dauert kommst du auf deine Sekunde.

Der schöne Günther 25. Mär 2013 20:16

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Wenn mich meine Erinnerungen nicht trügen, dann hat schon das
Delphi-Quellcode:
Delete()
selbst so lange gebraucht, obwohl das
Delphi-Quellcode:
EndUpdate()
noch gar nicht kam. Ich könnte mich aber auch irren. :oops:

Kann das jemand reproduzieren? Einfach ein paar hundert Zeilen in ein Memo kloppen und dann die vorderen mittels
Delphi-Quellcode:
Delete()
entfernen - Das ist doch ein schlechter Witz wie lange das dauert...

Amateurprofi 26. Mär 2013 00:27

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Vielleicht so:?
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
   Memo1.Clear;
   Memo1.Lines.BeginUpdate;
   for i:=1 to 10000 do Memo1.Lines.Add('Test '+IntToStr(i));
   Memo1.Lines.EndUpdate;
end;

procedure TForm1.Button2Click(Sender: TObject);
const cr=#13; lf=#10;
var count:integer; p,p1:PChar;
begin
   if Memo1.Text='' then exit;
   p:=PChar(Memo1.text);
   p1:=p;
   count:=0;
   repeat
      case p^ of
         cr : begin
                  inc(p);
                  if p^=lf then inc(p);
                  inc(count);
               end;
         lf : begin
                  inc(p);
                  inc(count);
               end;
         else inc(p);
      end;
   until (p^=#0) or (count=400);
   if p^=#0 then begin
      Memo1.Text:='';
   end else begin
      count:=(NativeInt(p)-NativeInt(p1)) div SizeOf(char)+1;
      Memo1.Text:=Copy(Memo1.Text, count,maxint);
   end;
end;

jaenicke 26. Mär 2013 07:43

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1208777)
Kann das jemand reproduzieren? Einfach ein paar hundert Zeilen in ein Memo kloppen und dann die vorderen mittels
Delphi-Quellcode:
Delete()
entfernen - Das ist doch ein schlechter Witz wie lange das dauert...

Kopiere den Text einfach in eine TStringList, lösche dort die Zeilen und kopiere den Text wieder zurück...

Ein visuelles Control wie ein TMemo ist nun einmal nicht zur Datenhaltung oder -bearbeitung gedacht, sondern nur zur Anzeige und Eingabe.

p80286 26. Mär 2013 11:01

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Zitat:

Zitat von jaenicke (Beitrag 1208822)
Ein visuelles Control wie ein TMemo ist nun einmal nicht zur Datenhaltung oder -bearbeitung gedacht, sondern nur zur Anzeige und Eingabe.

Dem ist nichts hinzuzufügen. Wenn Du aber unbedingt willst, dann arbeite von hinten nach vorne, das ist etwas schneller.

Gruß
K-H

Der schöne Günther 26. Mär 2013 11:09

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Aber warum ist es dann so blitzschnell wenn ich im Endeffekt ein
  • Setze Cursor nach ganz oben
  • Markiere die ersten 400 Zeilen
  • Entferne sie

mit der mysteriösen
Delphi-Quellcode:
Perform()
-Methode (siehe erster Beitrag) ausführe? :|

Sir Rufo 26. Mär 2013 11:40

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1208900)
Aber warum ist es dann so blitzschnell wenn ich im Endeffekt ein
  • Setze Cursor nach ganz oben
  • Markiere die ersten 400 Zeilen
  • Entferne sie

mit der mysteriösen
Delphi-Quellcode:
Perform()
-Methode (siehe erster Beitrag) ausführe? :|

Weil dann nur eine Aktualisierung ausgelöst wird und weil ein
Delphi-Quellcode:
TMemo
keine
Delphi-Quellcode:
TStringList
hat, sondern der Inhalt irgendwie als
Delphi-Quellcode:
TStrings
veröffentlicht wird. Je nachdem, wie dieses implementiert ist, geht das eben langsam oder schnell ;)

Ich vermute mal dass bei deinem Delete 400x diese Perform Funktion aufgerufen wird und das BeginUpdate, EndUpdate hier nicht greift

DeddyH 26. Mär 2013 12:36

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Ein Blick in die StdCtrls.pas wirkt manchmal Wunder (TCustomMemo.Lines ist vom Typ TMemoStrings, Zitat vorsichtshalber gekürzt):
Zitat:

Delphi-Quellcode:
procedure TMemoStrings.Delete(Index: Integer);
begin
  ... SendMessage(Memo.Handle, EM_LINEINDEX, Index, 0);
  ...
    ... SendMessage(Memo.Handle, EM_LINEINDEX, Index + 1, 0);
    if ... then
      SendMessage(Memo.Handle, EM_LINELENGTH, SelStart, 0);
    SendMessage(Memo.Handle, EM_SETSEL, SelStart, SelEnd);
    SendTextMessage(Memo.Handle, EM_REPLACESEL, 0, '');
  end;
end;


Horst_ 26. Mär 2013 13:14

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Hallo,

ich habe es mal mittels Lazarus unter Linux probiert und dort funktioniert memo1.lines.delete(0) sehr schnell.
Unter Win7 wurde es elend langsam.Warum, haben ja BUG und DeddyH aufgezeigt.
Amateurprofi's Ansatz ist etwas kryptisch. Ich habe Jaenicke's Idee aufgegriffen und das ist auch recht schnell und hoffentlich lesbar.
Delphi-Quellcode:
const
  MaxLineCnt = 5000;
  DelFirst  = 400;
procedure Tform1.Memofuellen(Sender: TObject);
var
   Formtxt : string;
   i,k : integer;
   TmpList : TStringList;
begin
  //Irgendeinen Text erzeugen mit laufender Nummer vorweg
  Formtxt :='%.8d';
  k := 64;
  i := length(Formtxt);
  Setlength(Formtxt,i+k);
  repeat
    inc(i);
    FormTxt[i] := chr(k+32);
    dec(k);
  until k < 0;

  i := MaxLineCnt;
  TmpList := TStringList.create;
  with TmpList do begin
    BeginUpdate;
    Capacity:= i;
    while i >= 1 do begin
      Add(Format(Formtxt,[i]));
      dec(i);
      end;
    EndUpdate;
    end;
  Memo1.lines := TmpList;
  TmpList.Free;
end;

procedure MemoZeilenentfernen( Ab,Anzahl:integer;var Mem:TMemo);
var
  i,k : integer;
  TmpList : TStringList;
begin
  TmpList := TStringlist.create;
  try
    With TmpList do begin
      AddStrings(mem.lines);
      k := Ab;
      i := Ab+Anzahl;
      // Ueber das Ende hinaus, dann begrenzen
      IF i > count then
        i := count;

      Beginupdate;
      while i < Count do begin
        Exchange(k,i);     //Zeigertausch
        inc(k);
        inc(i);
        end;
      //Loeschen von oben nach unten
      while i > k do begin
        dec(i);
        delete(i);
        end;
      EndUpdate;
      end;

    Mem.Lines := TmpList;
  finally
    TMPList.free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  T1,T0: TDateTime;
begin
   IF Memo1.lines.count = 0 then
     Memofuellen(self);
   IF memo1.lines.count<=DelFirst then begin
     memo1.lines.clear;
     EXIT;
     end;

   T0 := now;
   MemoZeilenentfernen( 0,DelFirst,memo1);
   T1 := now;

   Edit1.text := FormatDateTime('HH:NN:SS.zzz',T1-T0);
end;
Das funktioniert dann auch in unter 0.1 Sekunden statt 4,67 Sekunden mit 400-fachen memo1.lines.delete(0)

Gruß Horst
Update:
Ich habe mal eine ausgelagerte Prozedur daraus gemacht, mit der man auch innere Blöcke von Zeilen entfernen kann.
Bei Test mit 1e5 Zeilen dauerte die Übergabe an die Stringliste TmpList.AddStrings(memo1.lines) 1 Sekunde und das Löschen der ersten 400 Zeilen selbst unter 1 ms.
Man sollte also vielleicht generell die Daten in er seperaten Stringliste halten.

terence14 26. Mär 2013 13:29

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1208900)
Aber warum ist es dann so blitzschnell wenn ich im Endeffekt ein
  • Setze Cursor nach ganz oben
  • Markiere die ersten 400 Zeilen
  • Entferne sie

mit der mysteriösen
Delphi-Quellcode:
Perform()
-Methode (siehe erster Beitrag) ausführe? :|

Weil nach jedem Löschen einer Zeile eine Nachricht verschickt wird.
Du kannst das mit Memo1.BeginUpdate vor dem zeilenweisen Löschen und Memo1.EndUpdate wenn alle Zeilen gelöscht wurden, unterdrücken.

MfG,
terence

DeddyH 26. Mär 2013 13:31

AW: Warum ist das Löschen einer Zeile aus einer Memo so langsam?
 
Ein bisschen spät, gell? :mrgreen:


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:17 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