Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - RichEdit Lines Delete hängt sich auf


Knulli - Fr 21.06.13 16:39
Titel: RichEdit Lines Delete hängt sich auf
Hi Leute,
ich hab in nem RichEdit etliche Zeilen Text (Logging)
Nun will ich all diejenigen Zeilen rauswerfen, in denen ein bestimmter Text NICHT vorkommt.
Das klappt auch etliche Male und bei einer bestimmten Zeile hängt sich mein Programm auf. Es kommt aus RichEdit1.Lines.Delete(IDXZeile); nicht wieder.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
  CntZeile := RichEdit1.Lines.Count;
  IDXZeile := 0;

  while IDXZeile < CntZeile do
  begin
    Zeile := RichEdit1.Lines[IDXZeile];

    .....    

    if Not bHalten then
    begin
      RichEdit1.Lines.Delete(IDXZeile);
      Dec(CntZeile);
    end
    else
      Inc(IDXZeile);
  end;


Ich hab mal in die Sourcen reindebuggt, er kommt aus dem SendMessage(RichEdit.Handle, EM_REPLACESEL, 0, Longint(Empty)); nicht wieder.
Kennt das jemand? Es scheint übrigens an einer bestimmten Konstellation zu liegen. Lösche ich andere Zeilen, hängt er sich nicht auf.
Falls jemand Lust hat: Im Anhang die RTF, alle Zeilen löschen, außer die mit "DeviceId": "AnimalPort 3". Bei verbleibenden 258 Zeilen knallts.
RichEdit.Refresh zwischendurch hilft auch nicht :-(

Knulli


WasWeißDennIch - Fr 21.06.13 16:47

Was passiert denn, wenn Du eine TStringlist anlegst, die Zeilen aus dem RichEdit hineinkopierst, dann die betreffenden Zeilen aus dieser Liste löschst und zum Schluss alles wieder zurückschreibst?

[edit] Also ungefähr so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  Liste: TStringlist;
  i: integer;
begin
  RichEdit1.Lines.BeginUpdate;
  try
    Liste := TStringlist.Create;
    try
      Liste.Assign(RichEdit1.Lines);
      for i := Liste.Count - 1 downto 0 do
        if not Bedingung then
          Liste.Delete(i);
      RichEdit1.Lines.Assign(Liste);
    finally
      Liste.Free;
    end;
  finally
    RichEdit1.Lines.EndUpdate;
  end;
end;
[/edit]


Gerd Kayser - Sa 22.06.13 00:30

user profile iconKnulli hat folgendes geschrieben Zum zitierten Posting springen:
Im Anhang die RTF, alle Zeilen löschen, außer die mit "DeviceId": "AnimalPort 3". Bei verbleibenden 258 Zeilen knallts.


Mach's mit einer for-downto-Schleife:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.Button1Click(Sender: TObject);
var
  SuchText : string;
  Schleife : integer;
begin
  SuchText := '"DeviceId": "AnimalPort 3"';
  Label1.Caption := IntToStr(RichEdit1.Lines.Count);
  for Schleife := RichEdit1.Lines.Count - 1 downto 0 do
    if (Pos(SuchText, RichEdit1.Lines[Schleife]) = 0then
      RichEdit1.Lines.Delete(Schleife);
  Label2.Caption := IntToStr(RichEdit1.Lines.Count);
end;


jaenicke - Sa 22.06.13 08:50

Eine separate Stringliste wie von user profile iconWasWeißDennIch vorgeschlagen ist dennoch besser, da das deutlich schneller ist als die GUI Komponente ständig zu aktualisieren...

// EDIT:
Woher kommen die Daten eigentlich? Wenn die nicht vom Benutzer dort eingegeben werden, wäre es sinnvoller die zuerst zu bearbeiten und dann in die Richedit Komponente zu packen.


Gerd Kayser - Sa 22.06.13 11:05

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
da das deutlich schneller ist als die GUI Komponente ständig zu aktualisieren...

Dann setzt man halt ein "RichEdit1.Lines.BeginUpdate;" davor und ein "RichEdit1.Lines.EndUpdate;" danach. Ich kann mir jedenfalls kaum vorstellen, daß die Variante mit der StringList deutlich schneller ist.


jaenicke - Sa 22.06.13 13:36

user profile iconGerd Kayser hat folgendes geschrieben Zum zitierten Posting springen:
Dann setzt man halt ein "RichEdit1.Lines.BeginUpdate;" davor und ein "RichEdit1.Lines.EndUpdate;" danach. Ich kann mir jedenfalls kaum vorstellen, daß die Variante mit der StringList deutlich schneller ist.
Ich empfehle dir sehr einen Blick in den Quelltext von TRichEdit, dann würdest du das nicht sagen...
Kurz gesagt:
Zuerst wird der Anfang der Zeile über das Handle und den Zeilenindex von Windows erfragt, wenn diese existiert auch das Ende der Zeile. Dann wird dieser Teil des Textes wiederum über die Windows API markiert. Und schließlich wird dieser markierte Teil durch einen Leerstring ersetzt.

Was passiert bei TStringList:
Es wird anhand reiner Integervergleiche geprüft, ob der Index im gültigen Bereich liegt. Dann wird der Record mit den Daten des Eintrags gelöscht und die anderen Einträge um diesen freigewordenen Platz im Array verschoben. Es werden dabei nur wenige Bytes angefasst, da die Pointer auf die Strings verschoben werden. Die anderen Zeilen werden da gar nicht angefasst.

Probier es aus, wenn du es trotzdem nicht glaubst. Es ist deutlich schneller. Und zudem ist es auch sinnvoller solche Sachen ohne Beteiligung der GUI zu machen.


Gerd Kayser - Sa 22.06.13 15:07

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Es ist deutlich schneller.

Stimmt. Gerade mit der Datei des Fragestellers getestet. Meine Variante 0,163 Sekunden, die Variante mit der Stringlist 0,010 Sekunden (jeweils beste Zeiten aus mehreren Durchläufen genommen). Bei dieser (kleineren) Datei fällt das einem bei der Programmausführung aber kaum auf. Aber je größer die Datei ist, desto größer fallen die Zeitunterschiede aus. RichEdit wird da extrem langsam im Vergleich zur StringList-Variante.


Knulli - Do 27.06.13 13:45

So, erstmal Danke für die vielen Ideen.

Das mit einer zweiten Liste hab ich nicht gemacht, weil dabei die Text-Formatierungen verlorengehen. Das mit dem rot und blau hilft nämlich zusätzlich, den Überblick in den vielen Zeilen zu behalten.

Geschwindigkeit ist hier nicht so das Problem, da es um eine Fehleranalyse geht, es soll also aus den Log-Daten der Grund für ein Fehlverhalten der Anlage gewonnen werden.
Leider ist in den LogDaten eben auch viel anderes Zeugs mit drin.

Ich probier mal die Idee mit dem Löschen von Hintem aus.

Knulli


Knulli - Do 27.06.13 14:12

OK, so geht's... von hinten ist halt besser :lol:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
  IDXZeile := RichEdit1.Lines.Count - 1;
  while IDXZeile >= 0 do
  begin
    Zeile := RichEdit1.Lines[IDXZeile]; bHalten := false;
    JSONobject := TlkJSONobject(TlkJSON.ParseText(Zeile));
    if JSONobject <> Nil then
    begin
      stDeviceID  := GetValue('DeviceId');
      FreeAndNil(JSONObject);
      bHalten := stDeviceID = cboFilter.Items[cboFilter.ItemIndex];
    end;
    if Not bHalten then RichEdit1.Lines.Delete(IDXZeile);
    Dec(IDXZeile);
  end;


WasWeißDennIch - Do 27.06.13 14:20

Und was machst Du, wenn JSONobject nil wird? Wird dann gelöscht oder nicht?


Knulli - Di 23.07.13 15:43

Dann ist bHalten == false und die aktuelle Zeile wird gelöscht. Stand ja sowieso Müll (oder nix) oder eben nicht das gesuchte Wort drin...


jaenicke - Di 23.07.13 16:01

Nein, ist es nicht. Dann ist es nicht initialisiert...
Du setzt es nur in if JSONobject <> Nil then, liest es danach aber immer aus.

Der Compiler sollte da auch eine Warnung ausgeben.


Knulli - So 28.07.13 18:56

bHalten wird auch immer erstmal auf false gesetzt (Zeile über ParseText).


Der Compiler meckert nicht...


jaenicke - So 28.07.13 19:49

user profile iconKnulli hat folgendes geschrieben Zum zitierten Posting springen:
bHalten wird auch immer erstmal auf false gesetzt (Zeile über ParseText).
Hinten in der Zeile... ach du Schreck... :shock:
Wer Quelltext so formatiert... ohjemine. Ja, da hast du Recht, da steht es... aber durch die abenteuerliche Formatierung mit mehreren Befehlen in einer Zeile sieht man das halt nicht so schnell...


wq121984 - Di 06.08.13 07:30

This is a very good post, I like it very much