Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   StringList Textblöcke verschieben (https://www.delphipraxis.net/200749-stringlist-textbloecke-verschieben.html)

DieDolly 21. Mai 2019 20:32

StringList Textblöcke verschieben
 
Ich nutze eine StringList, um den Inhalt einer Textdatei zwischenzuspeichern und vorrätig zu halten. Für diesen speziellen Typ Dateiinhalt brauche ich kein Klassensystem oder sowas.

Wie verschiebt man am besten Textblöcke von denen ich die Indexe von Anfang bis Ende weiß, komplett an eine neue Position?
Ich hätte vielleicht eine Idee aber ich frage lieber hier nochmal nach, ob ihr eine saubere oder schon fertige Lösung kennt.

Uwe Raabe 21. Mai 2019 20:44

AW: StringList Textblöcke verschieben
 
Also sowas wie: Verschiebe zehn Zeilen ab Zeile 80 vor die Zeile 40?

DieDolly 21. Mai 2019 20:44

AW: StringList Textblöcke verschieben
 
Genau. Oder andersherum Zeile 80 bis 89 an Zeile 130 verschieben. Das sind jetzt nur zufällige Zahlen.

hoika 21. Mai 2019 20:47

AW: StringList Textblöcke verschieben
 
Hallo,
das sind Inserts (for-Schleife) und Deletes.
Wo ist das Problem?

DieDolly 21. Mai 2019 21:00

AW: StringList Textblöcke verschieben
 
Mein Problem ist, dass ich nicht weiß wie man das ordentlich umsetzt. Ich würde jetzt diese sagen wir mal 20 Zeilen in eine zweite StringList speichern und in die andere neu reinspeichern.

hoika 21. Mai 2019 21:54

AW: StringList Textblöcke verschieben
 
Hm,
früher hatten wir mal das

geg
ges

Ist doch nur ein bisserl InsertInto (=Copy) und danach das Entfernen des Org.Strings.

Ich würde einfach ein neues (leeres) Projekt machen und das dort Testen.
Button1Click->mach was

oder wer angeben will -> DUnit(xxx)

p80286 22. Mai 2019 09:12

AW: StringList Textblöcke verschieben
 
Ich würde das ungefähr so realisieren
Delphi-Quellcode:
for i:=startline+countlines-1 downto startline do
begin
  sl.insert(targetline,sl[i]);
  sl.delete(i);
end;
Ich hab nur keine Ahnung ob die Indizies passen und ob das Insert so funktioniert wie ich mir das denke.

Performanter wäre auf jeden Fall die Pointer auf den Text zu verschieben, da soweit ich weiß eine Stringliste intern ein Array of pointer to string. Aber so weit bin ich nie in die Sourcen vorgedrungen. Darum verwende ich immer noch gerne doppelt verkettete Listen.

Gruß
K-H

Delphi.Narium 22. Mai 2019 10:26

AW: StringList Textblöcke verschieben
 
Delphi-Quellcode:
for i:=startline+countlines-1 downto startline do
begin
  sl.insert(targetline,sl[i]);
  sl.delete(i);
end;
wenn targetline = 10
und i = 7
dann wird aus sl.insert(targetline,sl[i]); -> sl.insert(7,sl[10]);

Die nachfolgenden Zeilen werden dadurch nach "hinten" verschoben.

Die ursprüngliche Zeile 10 wird also zu Zeile 11.

Aus sl.delete(i); bzw. sl.delete(10); muss dann sl.delete(i + 1); also sl.delete(11); werden.

Deine Routine dürfte nur dann korrekt arbeiten, wenn targetline > startline ist.

Oder irre ich da?

Eventuell sowas in der Art?
Delphi-Quellcode:
procedure VerschiebeZeilen(sl : TStringList; AStartLine : Integer; ACount : Integer; ATargetLine : Integer);
var
  slTemp : TStringList;
  i     : Integer;
begin
  slTemp := TStringList.Create;
   // Die zu verschiebenden Zeilen sammeln.
  for i := AStartLine to AStartLine + ACount - 1 do slTemp.Add(sl[i]);
  // Nun die zu verschiebenden Zeilen löschen
  // Es wird immer AStartLine gelöscht, da dadurch die nachfolgenden Zeilen "nach vorne rutschen".
  for i := 1 to ACount do sl.Delete(AStartLine);
  // Und anschließend an der gewünschten Position einfügen.
  // Dabei wird mit der letzten Zeile der gesammelten Zeilen begonnen,
  // da diese bei jedem Einfügen einer neuen Zeile nach "hinten" verschoben werden.
  // Im Ergebnis bleibt dadurch die ursprüngliche Reihenfolge erhalten.
  for i := slTemp.Count - 1 downto 0 do sl.Insert(ATargetLine,slTemp[i]); // Hier war im Ursprung ein Fehler, es wurde sl[i] eingefügt, was absoluter Quatsch war und ist.
  slTemp.Free;
end;
(ungetestet hingedaddelt.)

p80286 22. Mai 2019 10:49

AW: StringList Textblöcke verschieben
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1432821)

Deine Routine dürfte nur dann korrekt arbeiten, wenn targetline > startline ist.

Davon bin ich ausgegangen! ???

Gruß
K-H

stahli 22. Mai 2019 11:42

AW: StringList Textblöcke verschieben
 
Ich könnte mir vorstellen, dass es i.d.R. performanter (und einfacher) ist, die Zeilen einfach komplett abschnittsweise in eine neue StringList zu kopieren.

Die Verschiebeaktionen mit Delete und Insert halte ich für recht (zeit)aufwendig.

Das konkrete Ergebnis hängt aber sicher auch immer von der Länge der StringList und des zu verschiebenden Blockes ab.

DieDolly 22. Mai 2019 11:45

AW: StringList Textblöcke verschieben
 
Die Blöcke haben maximal 30 Zeilen und die Datei ist bis zu 80000 Zeilen lang.

Delphi.Narium 22. Mai 2019 11:53

AW: StringList Textblöcke verschieben
 
Bei der Menge kann die Kombination aus Insert und Delete schon deutlich in die Laufzeit gehen.

DieDolly 22. Mai 2019 11:55

AW: StringList Textblöcke verschieben
 
Ich habe gerade das Beispiel von der vorherigen Seite getestet mit dieser Datei
Zitat:

1
2
3
4
5

6
7
8
9
10

11
12
13
14
15


Und diesem Aufruf und ich bekomme den Out of bounce Fehler
Delphi-Quellcode:
VerschiebeZeilen(sl, 5, 6, 18); // Block 5-10 soll nach 15 verschoben werden
Wenn ich 0, 5, 10 eingebe, kommt das hier als Ergebnis. Scheint etwas wackelig zu sein
Zitat:

6
7
8
9
10

11
12
13
14
6
7
8
9
10

15



Uwe Raabe 22. Mai 2019 11:57

AW: StringList Textblöcke verschieben
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1432821)
(ungetestet hingedaddelt.)

Du musst noch beachten, daß sich durch die sl.Delete Anweisungen der effektive Wert von ATargetLine ändern kann - nämlich dann, wenn AStartLine < ATargetLine ist.

Zitat:

Zitat von stahli (Beitrag 1432829)
Ich könnte mir vorstellen, dass es i.d.R. performanter (und einfacher) ist, die Zeilen einfach komplett abschnittsweise in eine neue StringList zu kopieren.

Das Insert/Delete ist lediglich abhängig von der Anzahl der Zeilen im Block (hier 30). Mit deinem Vorschlag müssen immer alle Zeilen (80000) angefasst werden.

Hier noch eine alternative Lösung (beachtet auch eventuell hinterlegte Objects):
Delphi-Quellcode:
    lst.BeginUpdate;
    try
      for I := 0 to ACount - 1 do
        lst.Insert(ATargetLine, '');
      if ATargetLine < ASourceLine then
        ASourceLine := ASourceLine + ACount;
      for I := 0 to ACount - 1 do
        lst.Exchange(ASourceLine + I, ATargetLine + I);
      for I := 0 to ACount - 1 do
        lst.Delete(ASourceLine);
    finally
      lst.EndUpdate;
    end;
Exchange tauscht intern nur die String-Pointer aus, ist also recht effizient.

Da beim Insert/Delete immer nur Leerstrings (Nil-Pointer) betroffen sind, fällt da auch kein verdeckter Verwaltungsaufwand an.

DieDolly 22. Mai 2019 11:59

AW: StringList Textblöcke verschieben
 
Zu diesem Beispiel habe ich die Frage, was denn ASourceLine ist?
Ist damit AStartLine gemeint? Wenn ja, verzeiht meine Unwissenheit :cyclops:

Obwohl, so ganz habe ich noch nicht verstanden wie da was verschoben wird. Meine Ergebnisse sind immer irgendwie komisch.

Delphi-Quellcode:
VerschiebeZeilen(sl, 0, 4, 8);
Zitat:

4

5
6
0
1
2
3
7
8
9

10
11
12
13
14



Moombas 22. Mai 2019 12:23

AW: StringList Textblöcke verschieben
 
Naja, 0 ist der Start, 4 das Ende (bei Zahlen 0-x ist Position 4 die "3" hier also 0-3) und schiebt es an Position 8 (bei Zahlen 0-x ist dies also 8-1 = da wo die 7 gestanden hat).

Das Ergebnis passt also.
Würdest du nun
Delphi-Quellcode:
VerschiebeZeilen(sl, 1, 5, 11);
schreiben, müsste folgendes das Ergebnis sein (Zahlen 0-14):
Vorher:
  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. 11
  13. 12
  14. 13
  15. 14
Nachher:
  1. 0
  2. 6
  3. 7
  4. 8
  5. 9
  6. 10
  7. 1
  8. 2
  9. 3
  10. 4
  11. 5
  12. 11
  13. 12
  14. 13
  15. 14

DieDolly 22. Mai 2019 12:30

AW: StringList Textblöcke verschieben
 
So funktioniert es bei mir. Alle Leerzeichen werden auch richtig gesetzt. Anders schaffe ich das nicht. Und selbst dann ist es n9icht zuverlässig.

Ich möchte ja keine Zeilen vertauschen sondern nur einen Block von oben irgendwo ganz nach unten verschieben.

Delphi-Quellcode:
var
 sl, slTemp: TStringList;
 i, LineStart, LineEnd, LineTarget: Integer;
begin
 sl := TStringList.Create;
 slTemp := TStringList.Create;
 try
  sl.Add('0 [');
  sl.Add('1');
  sl.Add('2');
  sl.Add('3');
  sl.Add('4');
  sl.Add(']');

  sl.Add('');

  sl.Add('5 [');
  sl.Add('6');
  sl.Add('7');
  sl.Add('8');
  sl.Add('9');
  sl.Add(']');

  sl.Add('');

  sl.Add('10 [');
  sl.Add('11');
  sl.Add('12');
  sl.Add('13');
  sl.Add('14');
  sl.Add(']');

  sl.Add('');

  sl.Add('15 {');
  sl.Add('16');
  sl.Add('17');
  sl.Add('18');
  sl.Add('19');
  sl.Add('}');

 // hier hin soll die 5-9

  sl.Add('');

  sl.Add('A');
  sl.Add('B');
  sl.Add('C');

  LineStart := sl.IndexOf('0 [');
  for i := LineStart to sl.Count - 1 do
   begin
    if sl.Strings[i] = ']' then
     begin
      LineEnd := i + 2;
      Break;
     end;
   end;

  LineTarget := sl.IndexOf('15 [');
  for i := LineTarget to sl.Count - 1 do
   begin
    if sl.Strings[i] = ']' then
     begin
      LineTarget := i + 2;
      Break;
     end;
   end;

  sl.SaveToFile('output1.txt');

  VerschiebeZeilen(sl, LineStart, LineEnd, LineTarget);

  sl.SaveToFile('output2.txt');
 finally
  slTemp.Free;
  sl.Free;
 end;
end;

Uwe Raabe 22. Mai 2019 12:34

AW: StringList Textblöcke verschieben
 
Zitat:

Zitat von DieDolly (Beitrag 1432835)
Zu diesem Beispiel habe ich die Frage, was denn ASourceLine ist?
Ist damit AStartLine gemeint?

Ja, da habe ich eine Inkonsistenz in der Benennung beseitigt: ASourceLine passt doch irgendwie besser zu ATargetLine, oder?

Delphi.Narium 22. Mai 2019 12:35

AW: StringList Textblöcke verschieben
 
Im ersten Versuch von mir war ein grober Fehler, habe ihn oben behoben.

Hier mal eine Version in PascalScript, sollte recht einfach übertragbar sein:
Delphi-Quellcode:
program Scriptname;

procedure VerschiebeZeilen(sl : TStringList; AStartLine : Integer; ACount : Integer; ATargetLine : Integer);
var
  slTemp : TStringList;
  i : Integer;
begin
  slTemp := TStringList.Create;
   // Die zu verschiebenden Zeilen sammeln.
  for i := AStartLine to AStartLine + ACount - 1 do slTemp.Add(sl[i]);
  // Nun die zu verschiebenden Zeilen löschen
  // Es wird immer AStartLine gelöscht, da dadurch die nachfolgenden Zeilen "nach vorne rutschen".
  for i := 1 to ACount do sl.Delete(AStartLine);
  // Und anschließend an der gewünschten Position einfügen.
  // Dabei wird mit der letzten Zeile der gesammelten Zeilen begonnen,
  // da diese bei jedem Einfügen einer neuen Zeile nach "hinten" verschoben werden.
  // Im Ergebnis bleibt dadurch die ursprüngliche Reihenfolge erhalten.
  for i := slTemp.Count - 1 downto 0 do sl.Insert(ATargetLine - ACount,slTemp[i]);
  slTemp.Free;
end;

var
  sl : TStringList;
begin
  sl := TStringList.Create;
  sl.Add('1'); sl.Add('2'); sl.Add('3'); sl.Add('4'); sl.Add('5');
  sl.Add(' ');
  sl.Add('6'); sl.Add('7'); sl.Add('8'); sl.Add('9'); sl.Add('10');
  sl.Add(' ');
  sl.Add('11'); sl.Add('12'); sl.Add('13'); sl.Add('14'); sl.Add('15');
  VerschiebeZeilen(sl,0,5,10);
  ShowMessage(sl.Text);
  sl.Free;
end.

Uwe Raabe 22. Mai 2019 12:42

AW: StringList Textblöcke verschieben
 
Zitat:

Zitat von DieDolly (Beitrag 1432838)
Ich möchte ja keine Zeilen vertauschen sondern nur einen Block von oben irgendwo ganz nach unten verschieben.

Du hast offenbar den Algorithmus noch nicht durchschaut. Deswegen erläutere ich das mal:

Zunächst füge ich an der Zielposition eine ausreichende Anzahl Leerzeilen ein:
Delphi-Quellcode:
      for I := 0 to ACount - 1 do
        lst.Insert(ATargetLine, '');
Dann korrigiere ich den Wert von ASourceLine, falls sich die entsprechenden Zeilen durch das Einfügen nach hinten verschoben haben:
Delphi-Quellcode:
      if ATargetLine < ASourceLine then
        ASourceLine := ASourceLine + ACount;
Nun tausche ich die zu verschiebenden Zeilen mit den gerade eingefügten Leerzeilen aus:
Delphi-Quellcode:
      for I := 0 to ACount - 1 do
        lst.Exchange(ASourceLine + I, ATargetLine + I);
Damit landen die zu verschiebenden Zeilen schon mal an der gewünschten Zielposition und an der ursprünglichen Position sind nun die Leerzeilen.

Im letzten Schritt werden diese Leerzeilen wieder gelöscht:
Delphi-Quellcode:
      for I := 0 to ACount - 1 do
        lst.Delete(ASourceLine);

DieDolly 22. Mai 2019 12:57

AW: StringList Textblöcke verschieben
 
Bei beiden Varianten lag mein Fehler glaube ich beim Aufruf

Delphi-Quellcode:
VerschiebeZeilen(sl, LineStart, LineEnd - LineStart, LineTarget); // LineEnd - LineStart statt nur LineEnd
Das Ergebnis sieht jetzt so aus
Zitat:

0 [
1
2
3
4
]

10 [
11
12
13
14
]

15 [
16
17
18
19
]

5 [
6
7
8
9
]

A
B
C


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