![]() |
Compare Files -> Optimieren
Liste der Anhänge anzeigen (Anzahl: 3)
Hi @all
hier wahrscheinlich mein letzter Beitrag in diesem Jahr... Ich habe mir ein kleines Programm geschrieben das rekursiv Dateien von zwei beliebigen Ordnern auf die Aktualität überprüft und das Ergebnis in ein StringGrid schreibt... Nun soweit ich es getestet habe funktioniert das ganze auch, bin aber mit der Geschwindikeit nicht zufrieden... Ich habe einen Geschwindikeitsvergleich mit dem Tool "Total Commander" gemacht und musste feststellen das mein Programm für den gleichen Vergleich immer etliche Sekunden länger braucht. [-> Vergleich mit 9591 Dateien in über 1000 Ordnern] Kurz zum Aufbau meines Programms: Zuerst werden beide angegebenen Ordner rekursiv nach Dateien abgesucht, das Ergebnis wird in einem Array festgehalten. Anschliessend werden die beiden Arrays verglichen... Viellecht müsste man die Methodik grundsätzlich überdenken?! Bin auf Eure verbesserungsvorschläge gespannt! Im Anhang das ganze Projekt... :cheers: Guten Rutsch ins neue Jahr |
Re: Compare Files -> Optimieren
Hallo Cherry,
Ich hab gerdade kein Delphi zur Hand darum nur eine theoretische Überlegung: zunächst, was willst Du mit dem Vergleich erreichen? 1) Du suchst gleiche Dateien mit gleichem Inhalt/gleichem Namen 2) Du suchst unterschiedliche Dateien in Verzeichnissen, die wahrscheinlich gleich oder weitestgehend gleich sind. Im ersten Fall steckst Du alle Dateinamen und die Dateigrößen in eine Tlist, sortierst nach der Größe und löschst alle Sätze wo es für die Größe keine Doublette gibt. Soll auch der Name gleich sein löschst Du auch die mit unterschiedlichen Namen. Was übrig bleibt, sind die vielleicht doppelten. Die nimmst Du Dir paarweise und liest die ersten 512...2048 Byte in zwei Arrays ein. Die Größe ist davon abhängig welche Dateien Du vergleichen willst, sprich wo wahrscheinlich der erste Unterschied auftaucht. Was danach noch übrig bleibt, davon erstellst du eine Prüfsumme (Hash) und wenn die gleich ist,dann hast Du eine inhaltsgleiche Datei. Im zweiten Fall erstellst Du zwei Listen, eine für das Quell- und eine für das Zielverzeichnist. Aus der Liste des Quellverzeichnisses generierst Du eine weitere Liste,die dem Inhalt des Zielverzeichnisses entsprechen sollte. vergleiche die Sollliste mit der Zielliste und Du erhälst die Dateien/Dateinamen die nur im Ziel oder Quellverzeichnis enthalten sind. Die übrigen vergleichst Du nach dem gleichen Schema wie oben, nur das Du diesmal die gleichen Dateien verwirfst und die ungleichen behälst. Grüße K-H |
Re: Compare Files -> Optimieren
Zitat:
|
Re: Compare Files -> Optimieren
Man muss sowieso nur die Dateien vergleichen, die eventuell identisch sein könnten. Dateien unterschiedlicher Größe und/oder unterschiedlichen Datums sind eh verschieden. Per Hash würde ich dann auch nicht vergleichen, sondern bis zum auftreten des ersten ungleichen Zeichens. Das dürfte schneller sein.
|
Re: Compare Files -> Optimieren
Hallo zusammen und ein gutes neues Jahr,
@alzaimar auf das gleiche/ungleiche Datum würde ich nicht vergleichen, da mir vor ein paar Jahren ein Novell-Server das Dateidatum "zerbröselt" hat. Auf's Bit gleiche Dateien hatten aus ungeklärten Gründen auf dem Server eine Stunde "Nachlauf", die dann zum Ausgleich beim kopieren auf den Client eine Stunde "Vorlauf" bekamen. Auch wenn die Zeit zwischen zwei Rechner nicht ordentlich syncronisiert, bekommst Du Probleme, darum Finger weg vom Datum(Zeit). was den Vergleich angeht, hast Du Recht, wenn zu erwarten ist, daß der überwiegende Teil der Dateien ungleich ist. Ist die Zahl der gleichen größer, oder der Unterschied liegt erst am "Ende", dann ist ein Hash schneller. (Zumindestens wenn ein erklecklicher Teil über 5-10 MB liegt) Grüße K-H |
Re: Compare Files -> Optimieren
so mache ich das:
(vorab Größe vergleichen und dann blockweise erstes unterschiedliches Zeichen suchen)
Delphi-Quellcode:
procedure TDCustom.CompareFileNow;
const BS = 1000; type TBB = array[1..BS] of Byte; var S1, S2: String; F1, F2: File of Byte; B1, B2: TBB; F: Boolean; R1, R2, I: Integer; begin ... S1 := FileName; S2 := PathCompare + CompareFileName; if (S1 <> '') and (S2 <> '') and (S1 <> S2) then begin if FileExists(S1) then begin ... if FileExists(S2) then begin F := True; AssignFile(F1, S1); Reset(F1); AssignFile(F2, S2); Reset(F2); if FileSize(F1) = FileSize(F2) then begin repeat BlockRead(F1, B1, Length(B1), R1); BlockRead(F2, B2, Length(B2), R2); if R1 = R2 then begin for I := 1 to R1 do begin if B1[I] <> B2[I] then begin F := False; Break; end; end; end else begin F := False; end; until (not F) or (Eof(F1) and Eof(F2)); end else begin F := False; end; CloseFile(F1); CloseFile(F2); FFileIsValue := F; // <--- end; end; end else ... end; stahli |
Re: Compare Files -> Optimieren
Zitat:
Zitat:
Bei einer Hashberechnung lese ich eine Datei *vollständig* ein und berechne einen Wert. Das mache ich für beide Dateien. Bei einem Dateivergleich lese ich auch beide Dateien Stück-für-Stück ein und vergleiche sie. Wo soll da der Geschwindigkeitsvorteil bei der Hashberechnung sein? Sind Hashbberechnungen neuerdings schneller als Vergleiche? :gruebel: Außerdem breche ich doch sowieso beim ersten ungleichen Byten ab. Bei einer angenommenen Gleichverteilung der Unterschiede lese ich also im Mittel nur 50% der Dateien ein. Der Einzige Vorteil eines Hashes wäre, wenn eine Datei A mit mehreren Dateien verglichen werden muss. Dann hätte ich einen Geschwindigkeitsvorteil. Im Übrigen bedeuten identische Hashes ja nicht notwendigerweise identische Inhalte. Die Wahrscheinlichkeit ist zwar 'eher' gering, aber rein mathematisch gesehen ist ein Dateivergleich per Hash ungenügend, ergo stimmt die E/A-Relation nicht, mithin ist das Programm dann beweisbar falsch. |
Re: Compare Files -> Optimieren
hallo alzaimar,
beim Datum hab ich mich wohl falsch ausgedrückt. Meine Annahme war gleiche Größe und unterschiedliches Datum=unterschiedliche Datei und das war falsch! Ich mußte also die Datei selbst vergleichen. Wie Du richtig ausgeführt hast, ist der Hash nur dann schneller wenn eine Datei mehrmals an einem Vergleich beteiligt ist. Dies ist bei mir der Fall, da z.b. bei drei Dateien mit der gleichen Größe jede Datei mit jeder anderen also (1 mit 2 und 3) und (2 mit 3) verglichen wird. Aber vielen Dank für die Anregung. Ich denke an der Vergleichslogik kann ich noch eine Menge feilen. Was den "Hash" angeht, verwende ich einen Prüfsummenalgorithmus, der vor Urzeiten mal in der Dr. Dobbs gestanden hat, der nach meinen Erfahungen für Dateien gleicher Größe bei ungleichem Inhalt auch ungleiche Werte produziert. Ich hab auch mal den "Adler" ausprobiert, aber bei dem sind Fehler aufgetreten. Mit MD5 habe ich auch noch keine Fehler erhalten, aber der ist etwas langsamer als "Dr.Dobbs". Da ich z.Zt. weit weg bin von meinem Delphi, kann ich nicht konkreter werden. @stahli: Entschuldigung Dich hätte ich beinahe vergessen. Du solltest BS ein wenig vergrößern. Ich glaube an die Magie der 2erPotenzen also 4096,8192 usw. versuchs mal damit Grüße K-H |
Re: Compare Files -> Optimieren
Anscheinend liegen einige Missverständtnisse vor...
Ich will in erster Linie die Dateien zweier nahezu identischen Ordnern auf ihre Aktualität überprufen. Und das übers Datum und nicht über den Inhalt der Dateien... Das ganze habe ich auch bereits realisiert, nur scheint mir meine Methodik nicht optimal da es offensichtlich auch schneller geht... Werft doch einfach mal einen Blick auf den Code...
Delphi-Quellcode:
Das ganze Projekt ist wie gesagt im ersten Beitrag gepostet...
...
// ]]] START COMPARING [[[ procedure TFormMain.Compare; var i,j,c,tmpIndex,tmpIndex2:Integer; Dir:String; // Directory Name arChecked: array of Integer; begin ClearStringGrid; SetLength(arFileRec1,0); SetLength(arFileRec2,0); SetLength(arChecked,0); Dir:=''; c:=0; // files count abort:=false; GetTreeList(DirectoryListBox1.Directory, DirectoryListBox1.Directory, '*', CbSubDirs.Checked, True, arFileRec1); GetTreeList(DirectoryListBox2.Directory, DirectoryListBox2.Directory, '*', CbSubDirs.Checked, False, arFileRec2); for i := low(arFileRec1) to high(arFileRec1) - 1 do begin // Application.ProcessMessages; // if abort then // exit; if (Dir <> arFileRec1[i].Dir) then begin Dir:=arFileRec1[i].Dir; Write(0,Dir,True); end; Write(0,arFileRec1[i].Name,True); Write(1,IntToStr(arFileRec1[i].Size)); Write(2,DateTimeToStr(arFileRec1[i].Date)); tmpIndex:=ArFileRecIndexOfDirName(arFileRec2,arFileRec1[i].DirName); Inc(c); if (tmpIndex <> -1) then begin // remember the values already checked ... SetLength(arChecked,Length(arChecked)+1); arChecked[Length(arChecked)-1]:=tmpIndex; // at least compare date values ... case CompareDate(arFileRec1[i].Date,arFileRec2[tmpIndex].Date) of fdsNewer: begin Write(3,'>'); end; fdsOlder: begin Write(3,'<'); end; fdsSameDate: begin Write(3, '=') end; end; Write(4,DateTimeToStr(arFileRec2[tmpIndex].Date)); Write(5,IntToStr(arFileRec2[tmpIndex].Size)); Write(6,arFileRec2[tmpIndex].Name); end else begin Write(3,'>'); tmpIndex2:=arFileRecIndexOfDir(arFileRec2,arFileRec1[i].Dir,0); if tmpIndex2 <> -1 then begin for j := tmpIndex2 to high(arFileRec2) - 1 do begin if not IntArray_Contains(arChecked,j) then begin if arFileRecIndexOfDir(arFileRec2,arFileRec1[i].Dir,j) <> -1 then begin if (arFileRecIndexOfDirName(arFileRec1,arFileRec2[j].DirName) = -1) and (arFileRec1[i].Dir = arFileRec2[j].Dir) then begin Write(3,'<',True); Write(4,DateTimeToStr(arFileRec2[j].Date)); Write(5,IntToStr(arFileRec2[j].Size)); Write(6,arFileRec2[j].Name); // remember the values already checked ... SetLength(arChecked,Length(arChecked)+1); arChecked[Length(arChecked)-1]:=j; Inc(c); end end else break; end; end; end; end; end; Dir:=''; // maby there exists folders unique on the right ... for i := low(arFileRec2) to high(arFileRec2) - 1 do begin // Application.ProcessMessages; // if abort then // exit; if not IntArray_Contains(arChecked,i) then begin if (Dir <> arFileRec2[i].Dir) then begin Dir:=arFileRec2[i].Dir; Write(0,Dir,True); end; Write(3,'<',True); Write(4,DateTimeToStr(arFileRec2[i].Date)); Write(5,IntToStr(arFileRec2[i].Size)); Write(6,arFileRec2[i].Name); Inc(c); end; end; Statusbar1.Panels[0].Text := ' files found: '+IntToStr(c); BtnCompare.Enabled:=True; BtnAbort.Enabled:=False; end; ... |
Re: Compare Files -> Optimieren
hallo Cherry,
Ich habe z.Zt. keine Möglichkeit Deinen Code herunter zu laden, darum nur so aus der Hüfte: Du verwendest sehr viele write, und die kosten Zeit! grüße K-H |
Re: Compare Files -> Optimieren
Neben dem Logging (per 'Write') würde ich folgende Operationen als Performancebremse ansehen:
1. 'GetTreeList' 2. 'arFileRecIndexOfDir'. Bei (1) hilft ein schnelleres Verfahren zum Auslesen einer Verzeichnisstruktur (gibts hier oder im der Delphi-Forum, weiss nicht mehr genau) Bei (2) wäre der Code interessant. Ich tippe auf eine einfache For-Schleife und das kann man wesentlich schneller machen, z.B. durch eine Hashmap. Und sowas gibt es als 'TStringDictionary' auch hier. |
Re: Compare Files -> Optimieren
Zitat:
Zitat:
zu 2, ja da hast du recht, is nur eine for schleiffe... werde mich mal mit sonem 'TStringDictionary' versuchen anzufreunden, habe ich allerdings noch nie verwendet. Ist das eine StdKomponente von Delphi? |
Re: Compare Files -> Optimieren
also, ch hab jetzt noch garnicht groß in deinen QuellCode reingesehn, aber beim ersten Testlauf bekam ich nach nichtmal 'ner Minute dieses hier
Zitat:
Speicher laut Taskmanager ~190 MB (von deinem Programm) und insgesamt noch knapp 1,5 GB frei Insgesamt würde es, von der Programmstruktur und den verwendeten Komponenten her, vermutlich aber eh Probleme mit vielen Dateien geben (war es wohl nicht sonderlich gut, von mir, deinem Programm gleich mal 2 "kleine" Festplatten zum Vergleich zu geben) Beim 2. Versuch, nur mit meinem USB-Stick und seinem Backup (je 26.506 Dateien und ~2 GB belegt) kommt der selbe Fehler. nja, auch wenn es nicht jeder baucht ... mit den japanischen, russischen und ähnlichen Dateinamen auf meinem Plättchen gib es sowieso Probleme. PS: Pegasus (Luckie), hSync (/me) und Weitere, derartige Programme findest in der DP [add] unter 100 Dateien in 'nem Verzeichnis scheint der Vergleich erstmal korrekt zu funktionieren |
Re: Compare Files -> Optimieren
Zitat:
Zitat:
Zitat:
|
Re: Compare Files -> Optimieren
Zitat:
|
Re: Compare Files -> Optimieren
Oh. Gut. Die Standard-Vorgehensweise. Hier oder im Delphi-Forum wurde ein kleiner Wettbewerb gestartet, welches Tool denn nun schneller ist (um Dateinamen zu suchen). Die Vorgehensweise könnte Dir jedoch helfen, denn 'FindFirst/FindNext' hat wohl einen ganz schönen Overhead.
Für den Rest habe ich derzeit leider keine Zeit. Heut Abend kann ich mal reinschauen. |
Re: Compare Files -> Optimieren
Zitat:
Zitat:
|
Re: Compare Files -> Optimieren
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
allerdings spare ich nicht allzuviel Zeit ein. Bei meinem vergleich mit ~10'000 und ~2'000 Ordnern war das Programm jetzt immerhin 2 Sekunden schneller und brauchte aber insgesammt immer noch 6 Sekunden. Das ist aber immer noch min. 2 Sekunden zu langsam und Zitat:
Delphi-Quellcode:
Im Anhang noch das aktualisierte Projekt...
// ]]] START COMPARING [[[
procedure TFormMain.Compare; var i,j,c,tmpIndex,tmpIndex2,ri:Integer; Dir:String; // Directory Name arChecked: array of Integer; arRow: TRowArray; begin ClearStringGrid; SetLength(arFileRec1,0); SetLength(arFileRec2,0); SetLength(arChecked,0); SetLength(arRow,0); Dir:=''; c:=0; // files count abort:=false; GetTreeList(DirectoryListBox1.Directory, DirectoryListBox1.Directory, '*', CbSubDirs.Checked, True, arFileRec1); GetTreeList(DirectoryListBox2.Directory, DirectoryListBox2.Directory, '*', CbSubDirs.Checked, False, arFileRec2); for i := low(arFileRec1) to high(arFileRec1) - 1 do begin // Application.ProcessMessages; // if abort then // exit; if (Dir <> arFileRec1[i].Dir) then begin Dir:=arFileRec1[i].Dir; SetLength(arRow,Length(arRow)+1); arRow[Length(arRow)-1].Name:=Dir; end; ri:=Length(arRow); SetLength(arRow,ri+1); arRow[ri].Name:=arFileRec1[i].Name; arRow[ri].Size:=IntToStr(arFileRec1[i].Size); arRow[ri].Date:=DateTimeToStr(arFileRec1[i].Date); tmpIndex:=ArFileRecIndexOfDirName(arFileRec2,arFileRec1[i].DirName); Inc(c); if (tmpIndex <> -1) then begin // remember the values already checked ... SetLength(arChecked,Length(arChecked)+1); arChecked[Length(arChecked)-1]:=tmpIndex; // at least compare date values ... case CompareDate(arFileRec1[i].Date,arFileRec2[tmpIndex].Date) of fdsNewer: begin arRow[ri].Sign:='>'; end; fdsOlder: begin arRow[ri].Sign:='<'; end; fdsSameDate: begin arRow[ri].Sign:='='; end; end; arRow[ri].Date2:=DateTimeToStr(arFileRec2[tmpIndex].Date); arRow[ri].Size2:=IntToStr(arFileRec2[tmpIndex].Size); arRow[ri].Name2:=arFileRec2[tmpIndex].Name; end else begin arRow[ri].Sign:='>'; tmpIndex2:=arFileRecIndexOfDir(arFileRec2,arFileRec1[i].Dir,0); if tmpIndex2 <> -1 then begin for j := tmpIndex2 to high(arFileRec2) - 1 do begin if not IntArray_Contains(arChecked,j) then begin if arFileRecIndexOfDir(arFileRec2,arFileRec1[i].Dir,j) <> -1 then begin if (arFileRecIndexOfDirName(arFileRec1,arFileRec2[j].DirName) = -1) and (arFileRec1[i].Dir = arFileRec2[j].Dir) then begin arRow[ri].Sign:='<'; arRow[ri].Date2:=DateTimeToStr(arFileRec2[j].Date); arRow[ri].Size2:=IntToStr(arFileRec2[j].Size); arRow[ri].Name2:=arFileRec2[j].Name; // remember the values already checked ... SetLength(arChecked,Length(arChecked)+1); arChecked[Length(arChecked)-1]:=j; Inc(c); end end else break; end; end; end; end; end; Dir:=''; // maby there exists folders unique on the right ... for i := low(arFileRec2) to high(arFileRec2) - 1 do begin // Application.ProcessMessages; // if abort then // exit; if not IntArray_Contains(arChecked,i) then begin if (Dir <> arFileRec2[i].Dir) then begin Dir:=arFileRec2[i].Dir; SetLength(arRow,Length(arRow)+1); arRow[Length(arRow)-1].Name:=Dir; end; ri:=Length(arRow); SetLength(arRow,ri+1); arRow[ri].Sign:='<'; arRow[ri].Date2:=DateTimeToStr(arFileRec2[i].Date); arRow[ri].Size2:=IntToStr(arFileRec2[i].Size); arRow[ri].Name2:=arFileRec2[i].Name; Inc(c); end; end; Statusbar1.Panels[0].Text := ' files found: '+IntToStr(c); BtnCompare.Enabled:=True; BtnAbort.Enabled:=False; StringGrid1.RowCount:=Length(arRow); for i := 1 to Length(arRow) do begin StringGrid1.Cells[0,i]:=arRow[i].Name; StringGrid1.Cells[1,i]:=arRow[i].Size; StringGrid1.Cells[2,i]:=arRow[i].Date; StringGrid1.Cells[3,i]:=arRow[i].Sign; StringGrid1.Cells[4,i]:=arRow[i].Date2; StringGrid1.Cells[5,i]:=arRow[i].Size2; StringGrid1.Cells[6,i]:=arRow[i].Name2; end; end; |
Re: Compare Files -> Optimieren
Zitat:
Die Klasse habe ich hier gefunden: ![]() Nur habe ich ein grosses Problem: Ich kappier nicht ganz wie die funzt. Zunächstmal wenn ich einen Wert adden will mit der Procedure Add(aKey: Cardinal; aData: Pointer) ... Wieso einen Pointer, ich will doch einen Stringwert hineinschreiben?! wie mach ich das nun? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:30 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz