Gelöst: Inhaltsvergleich TStringList

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
COMMANDER86
Beiträge: 20
Registriert: Di 23. Feb 2010, 01:11
OS, Lazarus, FPC: Win XP, Vista Busi.; L 0.9.28.2; FPC 2.2.4
CPU-Target: 32Bit

Gelöst: Inhaltsvergleich TStringList

Beitrag von COMMANDER86 »

Guten Morgen zusammen,

das ist mein erster Beitrag hier und ich hoffe, ich gehe mit euren Regeln konform. Also, folgende Problematik habe ich:

Ich habe drei StringListen:

Code: Alles auswählen

V_LIST: StringList vom Vorzustand
N_LIST: StringList vom "Nachher-Zustand"
LIST: StringList mit den Änderungen


Zur Vorgeschichte: Das Programm soll die Ergebnisse von standardmäßigen DIR Befehlen unter Windows auswerten. Sprich: Via "dir c:\ /s" wird ein komplettes Verzeichnis indiziert und schlussendlich in einer Datei abgelegt. Das geschieht zuerst in der V_LIST. Dann werden Änderungen an der Dateistruktur unter Windows vorgenommen und schließlich wieder ein "DIR-Abbild" erzeugt. Die Ergebnis-Dateien vom Vorher- und Nachherzustand werden beide verarbeitet und in weiteren Listen zusammengefasst:

So sieht die Liste vom DIR-Befehl aus:

Code: Alles auswählen

Verzeichnis von c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus
 
21.02.2010  23:51    <DIR>          .
21.02.2010  23:51    <DIR>          ..
21.02.2010  23:51             1.411 Lazarus (debug).lnk
21.02.2010  23:51               503 Lazarus entfernen.lnk
21.02.2010  23:51             1.229 Lazarus Forums.lnk
21.02.2010  23:51             1.243 Lazarus im Internet.lnk
21.02.2010  23:51             1.243 Lazarus Wiki Help.lnk
21.02.2010  23:51             1.346 Lazarus.lnk
               6 Datei(en)          6.975 Bytes


So sieht die Ergebnisliste aus:

Code: Alles auswählen

c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus (debug).lnk
c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus entfernen.lnk
c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus Forums.lnk
c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus im Internet.lnk
c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus Wiki Help.lnk
c:\Dokumente und Einstellungen\All Users\Startmen\Programme\Lazarus\Lazarus.lnk


V_LIST und N_LIST enthalten beide den letzten Stil an Daten. In N_LIST stehen allerdings mehr Einträge drin... es sind also Dateien hinzugekommen. Diese sollen nun ermittelt werden. Dazu habe ich verschiedene Lösungsansätze entwickelt:

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.Strings[V_LIST.IndexOf(N_LIST.Strings[I - 1])] = N_LIST.Strings[I - 1]
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;


Das war die Nummer, die unter Delphi bis dato (mit Memo-Feldern) funktioniert hat. Hier kommt bei Lazarus die Meldung: Index out of Bound (-1).

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.IndexOf(N_LIST.Strings[I - 1]) = (-1)
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;
 Hier rennt der Kollege scheinbar in einer Endlosschleife. Es gibt eine konstante CPU-Auslastung bei 50%, Speichernutzung liegt bei 20 MB und das Programm reagiert auf Nichts mehr... lässt sich nur noch beenden über Lazarus oder Taskmanager.


Alternativ habe ich folgende Version getackert:

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if (V_LIST.IndexOf(N_LIST.Strings[I - 1]) <> (- 1))
    and (V_LIST.Strings[V_LIST.IndexOf(N_LIST.Strings[I - 1])] = N_LIST.Strings[I - 1])
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;


Auch hier wird das zur "never ending story".

Grundsätzlich zu Info: Bis auf ein paar einzelne Einträge (deren Standort unbekannt ist) sind V_LIST und N_LIST vollkommen identisch. Bloß alle Änderungen (in Form von Einträgen, die in N_LIST vorkommen und NICHT in V_LIST) sollen in LIST eingetragen werden.

Ich hoffe, ihr könnt mir helfen. Falls Ihr weitere Codestücke oder Beispiele braucht: Bekommt Ihr.

Besten Dank und liebe Grüße
Fabian
Zuletzt geändert von COMMANDER86 am Di 23. Feb 2010, 15:39, insgesamt 1-mal geändert.
"Da programmierste ohne Fehler, alles funktioniert und dann benutzt irgendein User Vista."

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Inhaltsvergleich TStringList

Beitrag von mse »

COMMANDER86 hat geschrieben:

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.Strings[V_LIST.IndexOf(N_LIST.Strings[I - 1])] = N_LIST.Strings[I - 1]
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;


Das war die Nummer, die unter Delphi bis dato (mit Memo-Feldern) funktioniert hat. Hier kommt bei Lazarus die Meldung: Index out of Bound (-1).

Dies bedeutet, dass N_LIST.Strings[I-1] in V_LIST nicht gefunden wurde. Eine abenteuerliche Konstruktion. Falls ich den Code richtig verstehe, sollen in LIST alle Einträge hinzugefügt werden, die sowohl in N_LIST als auch in V_LIST vorkommen. Das wäre dann etwa so:

Code: Alles auswählen

for I:= 0 to N_LIST.Count - 1 do begin
 if V_LIST.IdexOf(N_LIST.Strings[I]) >= 0 then begin
  LIST.Add(N_LIST.Strings[I]);
 end;
end;


V_LIST und N_LIST enthalten beide den letzten Stil an Daten. In N_LIST stehen allerdings mehr Einträge drin... es sind also Dateien hinzugekommen. Diese sollen nun ermittelt werden. Dazu habe ich verschiedene Lösungsansätze entwickelt:


Dann eher etwa so:

Code: Alles auswählen

for I:= 0 to N_LIST.Count - 1 do begin
 if V_LIST.IdexOf(N_LIST.Strings[I]) < 0 then begin
  LIST.Add(N_LIST.Strings[I]);
 end;
end;

Nicht getstet!

Martin

Edit:
COMMANDER86 hat geschrieben:

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.IndexOf(N_LIST.Strings[I - 1]) = (-1)
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;
 Hier rennt der Kollege scheinbar in einer Endlosschleife. Es gibt eine konstante CPU-Auslastung bei 50%, Speichernutzung liegt bei 20 MB und das Programm reagiert auf Nichts mehr... lässt sich nur noch beenden über Lazarus oder Taskmanager.


Dies sollte funktionieren. Ich würde mal mit dem Debugger den Ablauf Kontrollieren.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Inhaltsvergleich TStringList

Beitrag von mschnell »

Um Stringlisten zu vergleichen, würde ich sie zunächst sortieren (mit .sorted:=true, oder gleich sortiert erstellen lassen). Dann kannst Du sie parallel durchlaufen, indem Du immer die Top-Elemente vergleichst. Ist bestimmt schneller als immer alle Elemente eine Liste abzufragen.

-Michael

COMMANDER86
Beiträge: 20
Registriert: Di 23. Feb 2010, 01:11
OS, Lazarus, FPC: Win XP, Vista Busi.; L 0.9.28.2; FPC 2.2.4
CPU-Target: 32Bit

Re: Inhaltsvergleich TStringList

Beitrag von COMMANDER86 »

Moin zusammen,

sorry... im ersten Part war ein Denkfehler von mir drin. So hätte es lauten müssen:

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.Strings[V_LIST.IndexOf(N_LIST.Strings[I - 1])] <> N_LIST.Strings[I - 1]
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;


Da kommt die Out of Bound Meldung. Ziel ist es, alle Einträge, die in N_LIST vorkommen, aber nicht in V_LIST, in LIST auszugeben.

Den Vorschlag von mse habe ich mal so abgewandelt (Zählung geändert; ist ja irrelevant - fehlt im Übrigen ein "n" von Index. ;) ):

Code: Alles auswählen

for I := 1 to N_LIST.Count do
  begin
    if V_LIST.IndexOf(N_LIST.Strings[I - 1]) < 0
    then LIST.Add(N_LIST.Strings[I - 1]);
  end;


Das läuft zwar nicht auf Fehler, aber bringt auch kein Ergebnis. Ich habe es nach sechs Minuten abgebrochen. Die Dateien haben rund 36000 Einträge - sollte also, denke ich, nicht so lange dauern.

mschnell hat geschrieben:Um Stringlisten zu vergleichen, würde ich sie zunächst sortieren (mit .sorted:=true, oder gleich sortiert erstellen lassen). Dann kannst Du sie parallel durchlaufen, indem Du immer die Top-Elemente vergleichst. Ist bestimmt schneller als immer alle Elemente eine Liste abzufragen.


Ich verstehe, was Du meinst. Mir ist allerdings noch nicht klar, wie ich das umsetzen soll. Klar sind die ersten Einträge gleich, aber irgendwann sind V_LIST[I] und N_LIST[I] ja an einem Punkt nicht mehr identisch und ab dann nie wieder. Da müsste ich also den Index anpassen, was in einer Schleife ja nun haarstreubend ist. Oder meinst Du ich soll die wegschreiben und rauslöschen, damit der Rest nachrutscht? Klingt possible... ich probier' das mal aus.

Danke schonmal, ich melde mich gleich wieder.

LG
Fabian
"Da programmierste ohne Fehler, alles funktioniert und dann benutzt irgendein User Vista."

COMMANDER86
Beiträge: 20
Registriert: Di 23. Feb 2010, 01:11
OS, Lazarus, FPC: Win XP, Vista Busi.; L 0.9.28.2; FPC 2.2.4
CPU-Target: 32Bit

Re: Inhaltsvergleich TStringList

Beitrag von COMMANDER86 »

Hallo nochmal,

kein Schiebeposting: Das Problem habe ich gelöst mit dem Ansatz von mschnell. Das sieht so aus:

Code: Alles auswählen

V_LIST.Sorted := True;
  N_LIST.Sorted := True;
 
  for I := 1 to V_LIST.Count do
  begin
    if V_LIST[I - 1] <> N_LIST[I - 1] then
    begin
      LIST.Add(N_LIST[I - 1]);
      N_LIST.Delete(I - 1);
 
      repeat
        if V_LIST[I - 1] <> N_LIST[I - 1] then
        begin
          LIST.Add(N_LIST[I - 1]);
          N_LIST.Delete(I - 1);
        end;
      until V_LIST[I - 1] = N_LIST[I - 1];
    end;
  end;


Beide Listen werden verglichen. Ist ein Eintrag nicht gleich, wird dieser weggeschrieben und anschließend aus N_LIST gelöscht. Wenn die Schleife nun weiterlaufen würde, würde der "nachgerutschte" Datensatz ja nicht überprüft werden. Deswegen die repeat until Schleife. Die fackelt den Punkt ab und läuft so lange durch, bis wieder Ordnung hergestellt ist. Ab an der Stelle die Sortierung notwendig ist, wage ich noch zu bezweifeln, da der Dir-Befehl ja auch nach einen Schema vorgeht.

Die Geschichte ist bei mir zunächst auch immer wieder auf Fehler gelaufen. Ich muss noch einen Check einbauen, denn: Die V_LIST war von gestern und die N_LIST von heute. Gestern hatte ich scheinbar mehr Dateien auf der Platte als heute... denke, das waren die Temp-Files, die beim runterfahren gekillt werden. Hab' jetzt Testweise XP SP3 entpackt... das sind 3000 und nochwas Dateien. Die habe ich nun sauber in der LIST mit ein paar Temp-Files vom IE und natürlich den N_LIST-Files, die beim V_LIST Durchlauf ja noch nicht da waren.

An dieser Stelle also Besten Dank! Problem gelöst.

LG
Fabian
"Da programmierste ohne Fehler, alles funktioniert und dann benutzt irgendein User Vista."

Benutzeravatar
theo
Beiträge: 10497
Registriert: Mo 11. Sep 2006, 19:01

Re: Gelöst: Inhaltsvergleich TStringList

Beitrag von theo »

Kleiner Hinweis:

Listen durchläuft man von 0 bis Count-1, dann kann man sich die I-1 sparen.
Also statt:
for I := 1 to V_LIST.Count do

for I := 0 to V_LIST.Count-1 do

COMMANDER86
Beiträge: 20
Registriert: Di 23. Feb 2010, 01:11
OS, Lazarus, FPC: Win XP, Vista Busi.; L 0.9.28.2; FPC 2.2.4
CPU-Target: 32Bit

Re: Gelöst: Inhaltsvergleich TStringList

Beitrag von COMMANDER86 »

theo hat geschrieben:Kleiner Hinweis:

Listen durchläuft man von 0 bis Count-1, dann kann man sich die I-1 sparen.
Also statt:
for I := 1 to V_LIST.Count do

for I := 0 to V_LIST.Count-1 do


Dem stimme ich zu... ist bei mir Macht der Gewohnheit und rührt aus der Vergangenheit... da musste ich Dateien auswerten, die so aussahen:

Code: Alles auswählen

Datensatz1
Feld1
Feld2
Feld3
Datensatz2
Feld1
Feld2
Feld3
...


Da bin ich dann mit einer Schleife drübergegangen, um die in eine CSV-Datei zu schreiben.

Code: Alles auswählen

for I := 1 to Memo1.Lines.Count do
begin
  Memo2.Lines.Add(Memo1.Lines.Strings[(I * 4) - 4] + ';' +
                  Memo1.Lines.Strings[(I * 4) - 3] + ';' +
                  Memo1.Lines.Strings[(I * 4) - 2] + ';' +
                  Memo1.Lines.Strings[(I * 4) - 1])
end;


Wenn man da mit 0 anfängt, funktioniert das logischerweise durch die Multiplikation nicht. Das Verfahren habe ich seitdem so beibehalten. Aber Du hast natürlich Recht. Ich könnte mir vorstellen, dass es mit "I" statt "I - 1" innerhalb einer Schleife sogar einen Bruchteil schneller wird, weil sich der Rechner die Umrechnung der Variable sparen kann... aber das ist nur eine Vermutung und bei heutigen Maschinen wohl auch kaum messbar, oder?

LG
Fabian
"Da programmierste ohne Fehler, alles funktioniert und dann benutzt irgendein User Vista."

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Gelöst: Inhaltsvergleich TStringList

Beitrag von mschnell »

Noch besser wäre es, die Listen statt von vorne von hinten aufzurollen, weil die Stringlist alle Elemente nachrückt, wenn man Element 0 löscht, wen nicht passiert, wenn man das letzte löscht.

-Michael

Antworten