Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Daten-Struktur für Backup-Tool
Flamefire - Do 11.09.08 11:24
Titel: Daten-Struktur für Backup-Tool
Hallo.
ich möchte ein kleines Backup-Tool machen. Dafür lese ich vor dem Vergleichen alle Dateien und Ordner in eine Struktur ein:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| TOrdner=record name:String; Ordner:Array of TOrdner; Dateien:Array of String; back:POrdner; end; POrdner=^TOrdner; |
Sollte an sich selbsterklärend sein.
Jetzt die Frage: Gibt es bessere Lösungen betreffs Speicher und Performance?
Wäre für alle Hinweise auch zu anderen Bereichen eines Backup-Tools dankbar.
Narses - Do 11.09.08 11:36
Moin!
Ich würde es mit Objekten und dem TObjectList-Container machen, um mir die Speicherverwaltung vom Hals zu halten. :nixweiss:
cu
Narses
Flamefire - Do 11.09.08 12:40
Hmm...und das soll schneller sein?
Ich mag sonst keine alzu fertigen Allround-Bausteine, da die meist eher Performance-Verlust bringen.
Diese Struktur lässt sich wunderbar durch rekurive prozeduren erstellen, bearbeiten und löschen.
Mit dem ObjectList habe ich noch nie gearbeitet...Ist der echte besser? Was kann der und wie benutzt man den?
BTW: Bei dem Array of String: Reicht da ein Setlength(Dateien,0); oder muss ich jeden String einzeln löschen?
Gausi - Do 11.09.08 12:48
Ich glaube, Narses meinte das in etwa so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| TOrdner = class name: String; Ordner: TObjectList; Dateien: TStringlist; back: TOrdner; end; |
Der Vorteil ist, dass TObjectlist recht schöne Methoden zum Einfügen und löschen bereitstellt, die du mit deiner Record-Array-Struktur alle selber bauen müsstest.
Zur Geschwindigkeit: Der Aufbau der Objekte im Speicher ist eh nicht der Flaschenhals so einer Anwendung. Der liegt eher im Ermitteln der Daten. ;-)
baka0815 - Do 11.09.08 12:50
Nimm lieber 'ne THashedStringList (unit IniFiles), die ist bedeutend schneller als die normale TStringList!
Flamefire - Do 11.09.08 12:54
ja ich weis dass das ermitteln schon lange dauert..aber wenn ich sagen wir 500.000 Dateien und 100.000 Ordner habe und für jeden Ordner statt nur den Pointer auf die Struktur ebn das ganze Zusätzliche Verwaltungszeug dran hängt, dass sich das sowohl in Geschwindigkeit als auch beim Speicher bemerkbar macht...
Obwohl Speicher: 600.000x~50Bytes=3MB
Geht also...
Und mir würde es mehr helfen, zu erfahren, warum das besser sein soll...Ich glaube nix was ich nicht verstehe ^^
Warum solln die TObjects besser sein als n Array?
Warum ist THashedStringList schneller als TStringList?
baka0815 - Do 11.09.08 12:58
Flamefire hat folgendes geschrieben: |
| Warum ist THashedStringList schneller als TStringList? |
Die THashedStringList macht das, was der Name schon sagt. Es werden Hashes über die Namen/Einträge gebildet und somit ist der Zugriff deutlich performanter als bei einer normalen TStringList.
Kann jedoch sein, dass das Einfügen in die Liste (aufgrund der Hash-Bildung) etwas langsamer ist, da kommt's dann auf den Fall genau an. Aber bei mehreren Hundert Dateien in einem Verzeichnis kann sich das positiv auswirken.
Gausi - Do 11.09.08 13:01
TObjectlist ist ein Array. Der Vorteil liegt daran, dass der ganze Bereich um die Speicherverwaltung (Objekte/Records erzeugen, einfügen, verschieben, löschen usw) über Methoden dieser Klasse erledigt werden kann. Und da wird mit einigen Tricks gearbeitet (Stichwort: Capacity), so dass das auch recht performant geschieht.
Flamefire - Do 11.09.08 13:17
ok...damit ich mich erst durch bergeweise dokumentationen arbeiten muss, kannst du mir n ganz einfaches bsp geben, wie ich das verwende?
also deklarationen
einfügen
zugriff
löschen
wulfskin - Do 11.09.08 13:42
Hallo Flammenfeuer,
tonnenweise Dokumentation? Nicht übertreiben!
Im Grunde genommen brauchst du nur die Methode
Add und
Delete und die Eigenschaft
Items. Hier ein kurzes Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| var List: TObjectList;
List.Add(Test);
List.Delete(List.IndexOf(Test)); Test.Free;
var I: Integer; begin for I := 0 to List.Count - 1 do MachWas(List[I]); end; |
Hab schon lange nicht mehr mit Delphi gearbeitet, deswegen kann ich nur hoffen das die Methoden-Namen noch stimmen.
Viel Glück,
Hape
Flamefire - Do 11.09.08 14:04
ok danke...
und "Test" kann ein beliebiges record sein?
warum sollte nu aber der zugriff über hashstringlist schneller sein?
wegen den hashes denke ich ist das nur wenn man einen eintrag sucht...nicht aber wenn man das alles nur über die (numerischen) indizes erledigt
Reinhard Kern - Do 11.09.08 17:48
Titel: Re: Daten-Struktur für Backup-Tool
Flamefire hat folgendes geschrieben: |
Hallo.
ich möchte ein kleines Backup-Tool machen. Dafür lese ich vor dem Vergleichen alle Dateien und Ordner in eine Struktur ein:
.... |
Hallo,
ich weiss ja nicht, was dein Backup-Tool alles können soll, aber das Einlesen der zu sichernden Struktur ist nicht unbedingt notwendig. Viele professionelle Tools arbeiten so, andere klappern aber einfach die Ordnerstruktur während des Backups ab. Dann kann man zwar nicht bzw. nicht ohne weiteres eine Fortschrittsanzeige machen, aber die Anzeige der bereits gesicherten Dateien und MBytes hilft auch schon.
Für das Restore gilt das gleiche: man speichert einfach Datei für Datei zurück, dann braucht man keinen Katalog.
Gruss Reinhard
Flamefire - Do 11.09.08 18:55
es geht um ein tool, das daten abgleichen soll...sprich: Datei fehlt, ist neu, ist geändert etc...
darum muss ich erst alles einlesen...
selbst wenn das nicht nötig sein sollte, finde ich es so besser...
EDIT: Habe ein Problem
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type POrdner=^TOrdner; TOrdner = class name: String; Ordner: TObjectList; Dateien: TStringlist; back: POrdner; end; |
...
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure delOrdner(var akt:TOrdner); var i:Integer; begin For i:=0 to akt.Ordner.Count-1 do delOrdner(TOrdner(akt.Ordner[i])); akt.Ordner.Free; akt.Dateien.Free; akt.name:=''; akt.Free; end;
procedure newOrdner(var akt:TOrdner;name:String='';last:POrdner=nil); begin akt:=TOrdner.Create; akt.name:=name; akt.Dateien:=TStringList.Create; akt.Ordner:=TObjectlist.Create; akt.back:=last; if(last<>nil) then last^.Ordner.Add(akt); end; |
newOrdner() funktioniert wundebar
aber das rekursive löschen geht nicht
kriege die Meldung: Konstantenobjekt kann nicht als var parameter übergeben werden
wie kann ich das nun machen?
Boldar - Do 11.09.08 21:27
Dann übergebe halt ein Pordner als Parameter!
Flamefire - Do 11.09.08 21:33
schon versucht mit
Delphi-Quelltext
1: 2: 3: 4: 5:
| procedure delOrdner(akt:POrdner); var i:Integer; begin For i:=0 to akt^.Ordner.Count-1 do delOrdner(POrdner(@akt^.Ordner[i])); |
Ebenfalls ne Fehlermeldung: "Variable erwartet"
Narses - Do 11.09.08 21:35
Moin!
Flamefire hat folgendes geschrieben: |
| Habe ein Problem |
Stimmt ;) du solltest dringend ein paar OOP-Tutorials lesen und dich mit der VCL-Doku beschäftigen. :|
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| type TOrdner = class name: String; Ordner: TObjectList; Dateien: TStringlist; back: TOrdner; end; |
Delphi-Quelltext
1: 2: 3:
| procedure delOrdner(var akt:TOrdner); procedure newOrdner(var akt:TOrdner;name:String='';last:POrdner=nil); |
Du brauchst einen vernünftigen Konstruktor und Destruktor der Klasse TOrdner, dann erledigt sich das Freigeben von ganz alleine, weil die TObjectList ihren Inhalt beim Freigeben mit entsorgt, wenn .OwnsObjects = TRUE ist (default).
Fazit: du mischt hier record/Pointer mit OOP, das wird nix. :?
cu
Narses
Flamefire - Do 11.09.08 22:02
gut dann habe ich tatsächlich keine Ahnung...
warum sollte sich die objectlist selber entsorgen und das noch rekursiv?
das muss ich doch alles selbst machen...woher soll der denn wissen was er machen soll?
wie müsste der constructor/destructor denn aussehen?
Die Rückrefernezierung ist dafür nötig, dass ich notfalls aus einem Eintrag den Kompletten Pfad wiederherstellen kann.
Und genau das meinte ich: Ich habe echt keinen Bock, mich durch mehr Tutorials und Dokus zu lesen, als mann verkraften kann. Man liest ne ewig lange Doku auf der Suche nach ner Kleinigkeit und stellt dann fest, dass es die falsche war...
Mit Klassen habe ich vorher selten gearbeitet...
Darum wäre es sinnvoller, einfach mal die Mini-Info zu geben als den Hinweis auf die Doku...
Meine 2 Prozeduren sind doch schon Konstruktoren/Destruktoren, nur dass ich die ebn nicht so gekapselt habe (in Create/Destroy)
Und wenn TOrdner schon ein Zeiger ist, warum muss ich den dann noch als referenz-Parameter übergeben?
Das ist ja bei Zeigern nicht nötig...
Narses - Do 11.09.08 22:08
Moin!
Flamefire hat folgendes geschrieben: |
| Und genau das meinte ich: Ich habe echt keinen Bock, mich durch mehr Tutorials und Dokus zu lesen, als mann verkraften kann. |
Wenn ich´s mir recht überlege :? ignorier meine Beiträge und bleib mal schön bei pointer/record... ;)
mit weniger als kostenlosem Privatunterricht darf man dem Herrn aber nicht kommen, was? "nene, so nicht, es muss schon schön auf mich zugeschnitten sein"
Viel Erfolg
Narses
Flamefire - Do 11.09.08 22:13
ok sry...werds mir mal angucken und dann posten...sag dann einfach obs richtig ist...
Narses - Do 11.09.08 22:16
Moin!
Flamefire hat folgendes geschrieben: |
| ok sry...werds mir mal angucken und dann posten...sag dann einfach obs richtig ist... |
Hey, das hätte ich von dir nicht (noch nicht/mehr) erwartet! :zustimm: Respekt. ;)
Ich werd´s mir dann anschauen.
cu
Narses
Flamefire - Do 11.09.08 22:26
nja bin z.zt. n bissl gestresst...sry wenn ich überreagiert habe...
Hab mal drüber nachgedacht:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| type POrdner=^TOrdner; TOrdner = class(TObject) name: String; Ordner: TObjectList; Dateien: TStringlist; back: POrdner;
public constructor Create(sname:String='';last:POrdner=nil); destructor Destroy; override; end; ... destructor TOrdner.Destroy; var i:Integer; begin Ordner.Free; Dateien.Free; name:=''; inherited Destroy; end;
constructor TOrdner.Create(sname:String='';last:POrdner=nil); begin inherited Create; name:=sname; Dateien:=TStringList.Create; Ordner:=TObjectlist.Create; back:=last; if(last<>nil) then last^.Ordner.Add(self); end; |
So weit OK?
Wenn Ich richtig liege wird bei "Ordner.Free;" für alle Ordner in der Liste "Free" aufgerufen und damit ist das schon rekursiv. Richtig?
Narses - Do 11.09.08 22:39
Moin!
Flamefire hat folgendes geschrieben: |
| Hab mal drüber nachgedacht: |
Na also, geht doch. ;)
Ich würde es aber trotzdem so ansetzen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| type TOrdner = class(TObject) private FOwner: TOrdner; FName: String; FOrdner: TObjectList; FDateien: TStringList; public constructor Create(const AName: String = ''; AOwner: TOrdner = NIL); destructor Destroy; override; property Owner: TOrdner read FOwner; property Name: String read FName write FName; property Ordner: TObjectList read FOrdner; property Dateien: TStringList read FDateien; end;
implementation
constructor TOrdner.Create(const AName: String = ''; AOwner: TOrdner = NIL); begin inherited Create; FName := AName; FOwner := AOwner; FOrdner := TObjectlist.Create; FDateien := TStringList.Create; if Assigned(FOwner) then FOwner.Ordner.Add(Self); end;
destructor TOrdner.Destroy; begin FreeAndNil(FDateien); FreeAndNil(FOrdner); inherited; end; |
Um einen solchen Baum freizugeben reicht es dann, den ersten Root-Ordner mit .Free freizugeben. Allerdings ist zu beachten, dass beim Löschen von einzelnen Unterordnern über eine (externe) Objekt-Referenz trotzdem ein Eintrag in der FOrdner-Objectlist des Owners verbleibt. Deshalb also besser immer über die Methoden der enthaltenden TObjectList löschen (.Delete oder .Remove). :idea:
Flamefire hat folgendes geschrieben: |
| Wenn Ich richtig liege wird bei "Ordner.Free;" für alle Ordner in der Liste "Free" aufgerufen und damit ist das schon rekursiv. Richtig? |
Exakt, darauf läuft es hinaus. :)
cu
Narses
Flamefire - Do 11.09.08 22:59
Ok sehr schön sauber so =)
Narses hat folgendes geschrieben: |
Allerdings ist zu beachten, dass beim Löschen von einzelnen Unterordnern über eine (externe) Objekt-Referenz trotzdem ein Eintrag in der FOrdner-Objectlist des Owners verbleibt. Deshalb also besser immer über die Methoden der enthaltenden TObjectList löschen (.Delete oder .Remove). :idea: |
Du meinst damit statt:
UnterOrdner.Free;
besser:
Delphi-Quelltext
1:
| UnterOrdner.Owner.Ordner[i].Free; |
Würde da das hier gehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| destructor TOrdner.Destroy; var tmp:TOrdner; begin tmp:=FOwner; if(tmp<>nil) then begin FOwner:=nil; tmp.Ordner.Remove(self) end else begin FreeAndNil(FDateien); FreeAndNil(FOrdner); inherited; end; end; |
Oder führt das möglichweise zur Zugriffsverletzung?
EDIT: Getestet: Logischerweise Zugriffsverletzung...Gibt es ne andre Möglichkeit? Sowas wie dass er die Zeile "tmp.Ordner.Remove(self)" in ne art warteliste stellt und ausführt, sobald er aus Destroy zurückkehrt?
Narses - Do 11.09.08 23:11
Moin!
Flamefire hat folgendes geschrieben: |
Du meinst damit statt: UnterOrdner.Free;
besser:
Delphi-Quelltext 1:
| UnterOrdner.Owner.Ordner[i].Free; | |
Nein, so:
Delphi-Quelltext
1: 2:
| UnterOrdner.Owner.Ordner.Extract(Unterordner); UnterOrdner.Free; |
oder gleich:
Delphi-Quelltext
1:
| UnterOrdner.Owner.Ordner.Remove(Unterordner); |
Wobei hier sicherzustellen ist, dass Owner auch nicht NIL ist!
Flamefire hat folgendes geschrieben: |
| Würde da das hier gehen: |
Nein, das
inherited Destroy; muss auf jeden Fall ausgeführt werden. Du könntest höchstens mit einem
if Assigned(FOwner) als erste Anweisung auf einen solchen reagieren und dich dann dort austragen lassen (macht die VCL bei den Components auch so). Allerdings musst du dann die beiden Fälle unterscheiden: a) einzelnes Objekt "ausnahmsweise" freigeben und b) einen Objektbaum abhacken (z.B. mit .Destroying: Boolean oder so machbar). Ich würde das aber lassen und ggfs. manuell die Referenz entfernen. ;)
cu
Narses
Flamefire - Do 11.09.08 23:31
Perfekt. Das Extrakt ist es:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| destructor TOrdner.Destroy; begin If(FOwner<>nil) then FOwner.FOrdner.Extract(self); FreeAndNil(FDateien); FreeAndNil(FOrdner); inherited; end; |
EDIT: Ist aber ineffizient, da die Suche und das Entfernen auch gemacht wird, wenn der ganze Baum gelöscht wird
Besser:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| destructor TOrdner.Destroy; begin FreeAndNil(FDateien); FreeAndNil(FOrdner); inherited; end; ... procedure TOrdner.Free(ARemove:Boolean=false); begin If(self<>nil) then begin If ARemove and (FOwner<>nil) then FOwner.FOrdner.Extract(self); Destroy; end; end; |
Kommt zwar ne Warnmeldung von Delphi aber das sollte so gehen. Oder hab ich was übersehen?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!