![]() |
Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Hallo zusammen,
ich möchte einen Verzeichnisbaum erstellen, welcher gleiche Dateien (PDF, JPG, PNG, ZIP, ...) eindeutig identifizieren kann und unabhängig vom Betriebssystem eingesetzt werden kann unter Windows, Linux (Web-Server), Macos, etc. den gleichen unverwechselbaren Fingerprint liefert. Mein Favorit wäre MD5, weil es am schnellsten ist und weil die Aufgabe nicht sicherheitskiritisch ist. Ich möchte nur möglichst schnell gleiche Dateien finden und zuordnen können, z.B. lokal und remote. Andere Optionen wären: Zitat:
Bei einer Kollision müsste man dann zusätzlich prüfen, ob es eine echte Kollision ist, oder nicht. Vielleicht ist die richtige Strategie dann MD5 - Kollision => Prüfe Datei binär. Hat jemand Erfahrung mit solchen Verzeichnisbäumen und Dateivergleichen, geht es noch effizienter? |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Für sowas nehme ich immer zuerst die Dateigröße und bei gleicher Dateigröße dann MD5. Gleiche MD5 und unterschiedliche Dateigröße heißt auch unterschiedliche Dateien. Eine fehlerhafte Erkennung von Gleichheit ist mir bei dieser Kombination noch nicht untergekommen.
Und wenn es definierte Dateitypen sind, wie eben PDF, JPG, PNG, ZIP, dann kannst Du anhand der ersten paar Byte der Dateien bei identischer Dateigröße und identischer MD5 noch damit prüfen, ob auch identischer Dateityp. Oder andersherum: Dateigröße, Dateityp aus den ersten paar Byte ermitteln. Wenn die übereinstimmen, dann noch MD5 (dürfte dann auch schneller sein, als Dateigröße, MD5 und dann erst den Dateityp) und wenn das dann alles gleich ist noch binären Vergleich auf Dateiebene. Die Wahrscheinlichkeit dann noch eine fehlerhaft Erkennung der Dateigleichheit zu "erwischen" dürfte deutlich geringer sein, als ein Sechser im Lotto ;-) |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Der Dateivergleich ist eine Sache, aber nicht unbedingt mein Hauptproblem, sondern wie man das Ganze in eine optimale Dateistruktur giesst, also einen Folder/FileTree ähnlich dem Explorer zum Beispiel. Ich sehe da möglicherweise zwei konkurrierende Pattern nebeneinander, einmal ein Dictionary für die Hashes um sehr schnell gleiche Dateien zu finden, und zum anderen z.B. eine TList als Baumstruktur, um die Files und Folderpfade abzubilden. Das würde ich gerne ein eine einzige Klasse oder Struktur bauen, die alle Funktionen abdeckt, ohne große Fehlermöglichkeit und Redundanz. Also Add/Get/Move/Delete (im Tree) aber auch Search (im Dictionary). Es gibt vielleicht eine ganz andere, offensichtliche Lösung, aber im Moment sehe ich den Wald vor Bäumen nicht. Klar, es ist wohl schon Freitag, daran muss es liegen :stupid: |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Egal welchen Algo du nimmst, Hauptsache ist, dass du nicht die Implementierung in der RTL nutzt :lol:
|
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Ja, selbst das würde mir erstmal reichen.
Aber ja, Spring4D sollte es schon sein :thumb: Gibt es denn da eine gute Collection, welche Tree mit Dictionary/Map von Haus aus sauber abbildet? Ich könnte natürlich nur eine Dictionary verwenden und darin irgendwie den Pfad mit speichern und mich irgendwie durchhangeln. Gefühlt erscheint mir das aber erstmal nicht besonders effizient, für die Verwaltung einer Baumstruktur. Oder ist das Ganze etwa besser mit JSON-Nodes zu erreichen? |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Die Daten bilde ich immer in 'ner Datenbanktabelle ab, ggfls. auch "nur" 'ne Memorytable. Da bekomme ich immer "für umsonst" die entsprechenden Möglichkeiten für die Auswahl nach welchen Kriterien auch immer und muss nicht erst eine wie auch immer geartete Struktur "erfinden" und alle für die Selektion, Auswertungen, ... nötigen Algorithmen implementieren. (Je Datei eine Zeile in der Tabelle.)
Die Optik wird dann aus den selektierten Daten gebildet. Aber vermutlich gehen wir hier grundsätzlich unterschiedlich an die Problemlösung heran. JSon, XML, Collctions, ... sind mir hier gedanklich und aufwandstechnisch viel zu kompliziert. Das liegt vermutlich auch daran, dass ich nur Delphi 7 hab' und bei mir fast jede Anwendung irgendwann bei der Datenhaltung in 'ne Datenbankanwendung "ausartet" (meist mit KBMemtable) und nichts davon (seit ca. einem Jahrzehnt) im professionellen Bereich zum Einsatz kommt. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Ich würde auch Parameter, welche rasch zu gewinnen sind früh auswerten und jene, welche Kosten verursachen erst spät. Der Informationsgehalt der verschiedenen Parameter muss natürlich mitberücksichtigt werden.
Du hast für viele Dateien mehrere Parameter (Dateigrösse, MD5, SHA256..., nennen wir sie mal alle Hashes, obschon das für gewisse Typen genau genommen nicht zutrifft) Werte gewonnen und willst wissen, ob eine weitere Datei bereits vorhanden ist? Eventuell ist es ratsam vorher gewisse (Nicht Hash konforme wie Dateigrösse) Parameter p1,p2,... zu einem Parameter p zu hashen und dann... => ![]() Du suchst OS übergreifend. Dennoch der Link für Windows auf ![]() |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Ich bin bisher davon ausgegangen, dass es wohl zu langsam wäre, aber stimmt, das muss es ja gar nicht. Wie verwaltest Du denn da die Baumstruktur, mit selbst-referenzierten Keys in einer Tabelle? Vielleicht hast Du dafür schon eine optimale SQL-Struktur gefunden, Baumstrukturen in einer DB sind etwas problematisch. Mit FDMemTable könnte das schon ziemlich performant sein, vielleicht sogar mit sqlite, was noch weitere Vorteile hätte. Was mich bisher nicht auf den Gedanken gebracht hat, war, das ich sowas wie ein Einlesen aller Verzeichnisse oder ein Hinzufügen von Verzeichnissen einbauen möchte. Mit Unterverzeichnissen und Dateien, da könnte eine DB etwas schwächeln, gegenüber einen optimierten Speicherliste mit Hash-Option. Eigentlich wollte ich die Struktur immer on-the.fly laden, und dabei auf Änderungen prüfen, aber ja, eine permanente DB würde das regelmäßige Einladen sparen. Zitat:
Danke für die Vorschläge mit den zusätzlichen Parametern, aber im Moment reichen mir die JA/NEIN Aussagen zu Files welche geändert wurden, völlig aus. Zusätzlich dazu wäre dann noch entsprechend der letzte Zuugriff interessant, aber optional. Weiterhin war mein Gedanke, dass diese Struktur dan auch gleich das Navigieren in den Dateien übernehmen kann. Also eine Klasse für Verzeichnisstruktur mit Navigation und schneller Hash-Suche. Womöglich ist aber auch eine Trennung beider "Concerns" sinnvoller, gefühlt würde ich momentan aber eher alles in eine Baumstruktur packen, um nicht noch viel Redundanz und Komplexität beim Aufrufer reinzubekommen. Ausserdem wäre eine zentrale Klasse sicher fehlertoleranter auszulegen als zwei separate, die sich überschneiden können. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Hi,
Sorry if i am missing the subject, I would like to suggest to skip any multi parameters, and use concatenation, example: MD5 could be more than enough and as "Delphi.Narium" mentioned use the file size this will make collision probability a lot smaller, but prefix the hash with the (aligned to 4 byte by zeros) size like this : the MD5 hash for "This is MD5" = f6eda1d8b4b2dba89938db14285cf78a Length("This is MD5") = 11 then your custom hash will be 0000000bf6eda1d8b4b2dba89938db14285cf78a The advantage here : 1) removing the need for multiple parameters. 2) reduce the collision chance. 3) in its binary (non hex format) it can be used in Trees or DBs .. with only 20 bytes length. 4) if you have files size that need 64bit then use the lower 32bit only and it will be fine, this doesn't affect the collision chance, if there is too many big files then use 5 bytes for the file size, but this really not needed. 5) and as Stefan did point, use an optimized implementation for MD5. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Also ganz kurz (oder eher doch etwas lang geworden ;-)):
Es gibt eine Komponente für das rekursive Einlesen der Dateien. Je Datei wird ein Ereignis OnFile ausgelöst. In dem Ereignis wir der Dateiname an eine Komponenten übergeben, die die Datei liest und Dateigröße, MD5, Dateityp (sofern ermittelbar), ... in Attributen zurückgibt. Danach wir für jede Datei genau ein Datensatz in der Tabelle (egal ob Datenbank oder Memorytable) angelegt (Spalten: einfach alle Werte, die ich im späteren Verlauf benötige(n könnte). Per AutoInc wird ein eindeutiger technischer Schlüssel festgelegt, mit dem kann man dann jederzeit jede Datei eindeutig identifizieren, ginge zwar auch über den vollständigen Dateinamen (der immer 'nen eindeutigen Index hat) ist per AutoInc aber einfacher zu handhaben, da ggfls. auch mal das Tag-Attribut einer Komponente ausreichen kann, um den Schlüssel jederzeit zur Verfügung zu haben. Damit ist die Datenhaltung erledigt. Und sofern ich 'ne Datenbank nutze, muss ich auch nicht immer alle Daten im Arbeitsspeicher haben, sondern nur das, was ich gerade für 'ne Auswertung auch benötige. Auswertungen erfolgen über SQL oder Filter, je nach dem, was Datenbank oder Memorytable gerade unterstützen. Da mich für gewöhnlich nur die Dubletten interessieren, komme ich mit 'nem
Delphi-Quellcode:
aus. Alles was da dann vorkommt, kann per Filter oder Select in weiteren Abfrage (oder etwas komplexeren SQLs) ausgewählt werden. Und nur aus dem so erstellten Ergebnis wird dann die Anzeige zusammengebaut. Für 'nen Tree muss man dann ggfls. die Pfadangabe am PathDelimiter aufbröseln, um den Baum optisch korrekt zu erstellen. Aber die ganze Baumstruktur für 'ne Million Dateien aufzubauen, nur um dann festzustellen, dass ich eventuell irgendwo 'ne Dublette haben könnte (oder eben auch keine), ist mir zu aufwändig.
Select MD5 from tabelle having count(*) > 1
Sind die Zeitstempel der Datei mit in der Tabelle, kann ich so auch auf Dateiänderungen prüfen und die entsprechenden Werte in der Tabelle ändern. Ist 'ne Datei schon in der Tabelle, muss ich nicht bei jedem Prüfvorgang alles neu einlesen, sondern nur das Neue oder das Veränderte. Das kann dann (je nach Datenmenge und Datenträgergeschwindigkeit) das eine oder andere Kaffeezwangspäuschen obsolet machen. Kommt alles in 'ne Datenbanktabelle, muss ich aber auch ab und an mal prüfen, ob's das, was in der Tabelle steht, im realen Leben noch gibt und nicht inzwischen gelöscht wurde. Spätestens bei Auswertungen, die Dubletten erkannt haben, muss man dann noch mal auf die Existenz der Dateien prüfen, um nicht z. B. umbenannte Dateien mit altem und neuem Dateinamen als Dubletten zu identifizieren. Es ist also nicht ganz banal, aber vermutlich einfacher, als mit komplexen Baumstrukturen im Arbeitsspeicher zu hantieren. In kurz: Einmal alles in 'ne Datenbanktabelle und dann per SQL auswerten. Wenn es dann tatsächlich was zu prüfendes, sprich Dubletten, gibt, kann man sich um eine entsprechende Optik kümmern. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Ich möchte deine Aufmerksamkeit auf
![]() ![]() ![]() Ich fand es auch günstig, zunächst nur den Anfang zweier Dateien zu vergleichen, insbesondere bei sehr großen Dateien. Da unter Windows immer mindestens 256 KB eingelesen werden, lese und vergleiche ich zunächst einmal diese ersten 256 KB, was schon einmal so gut wie alle nicht gleichen Dateien aussieben sollte. Bei mir sind Hardlinks ein Thema, weswegen ich auch mittels
Delphi-Quellcode:
die FileID ermittle und vergleiche.
GetFileInformationByHandle
Mir haben auch die Hinweise von ![]() ![]() |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Es geht hier doch garnicht um extreme Sicherheit,
es geht auch nicht um Vergleiche "ähnlicher" Dateien, sondern nur um Ändernung jeweils einzelner Dateien mit sich selbst, womit die Möglichkeit extrem selten vorkommender Hash-Kollisionen sehr unwahrscheinlich ist, vor allem, wenn niemand (Hacker und Co.) absichtlich die Datei gezielt und mit enormem Aufwand daraufhin ändert. * das Archiv-Attribut * Datum des letzten Schreibzugriffs * und ansonsten sind doch immernoch MD5/SHA1/SHA256 ausreichend :roll: System.Hash : THashMD5 (128) THashSHA1 (128) THashSHA2 (224,256,384,512) THashBobJenkins (32, verwendet Dlelphi für Listen) |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Hab' einfach mal wieder die KI meiner Wahl befragt:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Eine Tabelle mit Dateinamen, Dateigröße, MD5-Hash und den Änderungsdaten sollten für die gestellte Aufgabe ausreichend sein. Das Erstellen einer Baumstruktur ist nicht erforderlich, es sei denn, man möchte es für die Anwender schön aussehen lassen. Dann reicht es aber aus die Baumstruktur nur für die Daten zu erstellen, die die Anwender auch zu Gesicht bekommen sollen und zwar erst dann, wenn die Daten zur Anzeige gebracht werden. Mein Vorgehen wäre: Rekursives Einlesen der Verzeichnisstruktur(en) und je Datei Pfad und Name, Dateigröße und Änderungsdaten in einer Tabelle speichern, dazu reichen FindFirst und FindNext mit TSearchRec aus. In der so erstellten Tabelle alle Sätze suchen, bei denen die Dateigröße mehr als einmal vorkommt:
SQL-Code:
Für die so ausgewählten Dateien den MD5-Hash berechnen und in der Tabelle speichern.
select * from tabelle a where exists (
select 1 from tabelle b where a.dateigroesse = b.dateigroesse group by b.dateigroesse having count(*) > 1 ) order by a.Dateigroesse; Anschließend die Dateien suchen lassen, bei denen die Kombination aus Dateigröße und MD5-Hash mehr als einmal vorkommen.
SQL-Code:
Aus dem Ergebnis wird dann die Anzeige für die Anwender befüllt, egal ob als TreeView, ListView oder auch einfach nur in 'nem DBGrid.
select * from tabelle a where exists (
select 1 from tabelle b where a.dateigroesse = b.dateigroesse and a.md5 = b.md5 group by b.dateigroesse, b.md5 having count(*) > 1 ) order by a.Dateigroesse, a.md5; |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Hallo Delphi.Narium,
ich bin nicht ganz sicher ob Du nur die Hash-Verwaltung beschreibst, oder ob Hash und Tree-Verwaltung zusammen integriert sind. Ich hatte ja schon geschrieben, dass dies vielleicht zwei verschiedene Aufgaben sind, die man nicht mischen sollte. Persönlich fände ich das Mischen beider Aufgaben aber sehr sinnvoll, eben weil es dann keine Redundanzen und Fehler durch getrente Datenhaltung auf das gleiche physikalische System geben kann. Ich meine Du beschreibst das aufnehmen der Hashes in die DB usw., und dann nur am Rande, dass daraus Tree, ListView usw. erstellt werden können. Welche Struktur schlägst Du denn dafür vor? Ich sehe dafür erstmal zwei sinnvolle Optionen in einer DB, vielleicht gibt es aber noch weitere:
Delphi-Quellcode:
In jedem Fall ist der "hash_value" sozusagen nur ein nützliches Abfallprodukt, mit dem sehr schnell verifiziert werden kann,
-- Adjacency List
CREATE TABLE FileSystem ( id INT PRIMARY KEY, parent_id INT, name VARCHAR(255), hash_value VARCHAR(255), type VARCHAR(50), FOREIGN KEY (parent_id) REFERENCES FileSystem(id) ); -- Nested Set CREATE TABLE FileSystem ( id INT PRIMARY KEY, name VARCHAR(255), lft INT, rgt INT, hash_value VARCHAR(255), type VARCHAR(50) ); ob es eine Datei bereits im System gibt und wie oft, unabhängig von der zu Grunde liegenden Baumstruktur. Das trifft das was ich suche schon ganz gut, danke sehr für die Idee mit der DB, das scheint eine Menge Vorzüge über einer spezifischen Klasse zu haben. Meine Frage war zu Deiner Erfahrung mit "Adjacency List" bzw. "Nested Set" oder eventuell auch "Flat table" mit kompletten Pfadangaben, was davon sich für das Durchlaufen von Filesystemen am besten eignet. Ich muss wahrscheinlich öfters die gesamten Filesysteme abgleichen, eben weil es mehrere Parteien gibt, welche unabhängig voneinander darauf zugreifen können. Remote-Verzeichniss (es kann mehrere geben) - ist ein zentrales Filesystem mit entfernten Daten, gehostet auf einem Fileserver im Internet - es kann durchaus mehrere, redundante oder auch ergänzende Remote-Verzeichnisse geben, auf verschiedenen Servern - dieses kann auf verschiedenen Wegen bearbeitet werden (z.B. automatisch aus einem anderem Dokumentenmanagementsystem, manuell per Website) - kann über verschiedene Protokolle bearbeitet werden (z.B. FTP, REST-API, direkt über WebClient auf dem Serversystem) - das von verschiedenen Parteien aus bearbeitet werden kann (automatische Ausleitungen von verschiedenen Systemen) - eine Verwaltung von Änderungen, Verzeichnisbaum auf dem Server ist erstmal nicht so ohne Weiteres möglich. - die Möglichkeit eine DB auf dem Server zu halten, welche das Ganze zentral abbildet, wäre denkbar ist aber auch eher ungewünscht. Lokal - ist eine lokale Kopie zur Zusammenführung, Bearbeitung und Analyse spezifischer Daten (lokal um den Server nicht zu belasten) - die lokale Kopie sollte möglichst nur bei Änderungen aktiv werden, daher die Frage nach Vergleich ganzer Baumstrukturen mit Hash - insbesondere das Einstellen neuer Dateien soll abgeprüft und verhindert werden (das gleiche File, mit anderem Namen, an andere Stelle). Eine Möglichkeit wäre noch das Erzeugen und Speichern von Fingerprints (*.md) auf den Servern, was aber auch eher ungewünscht ist. Die Datenmengen sind jedenfalls nicht so groß, dass eine Synchronisation Remote-Lokal generell ein Problem wäre. Deshalb trifft der Vergleich zu GIT/GitHub von Benmik schon ganz gut zu, nur eben geht es in erster Linie um binäre Dateien, nicht nur um Text. Es gäbe noch einen anderen Vergleich, z.B. mit einem FTP-Client, welcher auch Locale und Remote Filesysteme abgleichen kann, aber nicht unbedingt Änderungen in den Files erkennen kann. Ich denke der Ansatz mit einer DB ist einen Versuch Wert, das werde ich mal nächste Woche angehen. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Habe momwentan keine Zeit ausführlich zu antworten, bitte hab' da etwas Geduld.
Ja, ich beschreibe nur die Hash-Verwaltung. Einen Baum halte ich für überflüssig. Mir erschließt sich nicht, wofür er nützlich sein sollte, außer für die Anzeige der Daten. Dann kann man ihn mit 'nem Treeview gezielt aus der benötigten Teilmenge der Tabelle erstellen. Die Baumstruktur für die Anzeige kann man immer "on the fly" aus den Pfadangeaben und dem Dateinamen erstellen und muss sie nicht permanet vorhalten, zumal sie für das Erkennen von Dubletten, ... keinerlei Mehrwert hat. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Selbst wenn der Hash gleich groß wäre, wie die Datei, gäbe es theoretisch immernoch Kollisionen, da ja der Hash einen anderen Wert berechnet, wie die Ursprungsdaten und somit kann ebenfalls bei unterschiedlichen Dateien der Selbe hash entstehen. Was also bei den Hashs den Unterschied macht, ist wie groß er ist, je größer um so unwahrscheinlicher, und je besser die Berechnung ist, um unwahrscheinlicher. Billigster Hash, es werden einfach nur die Bytes in den Speicher geschoben, ohne Rückführung des Überlaufs, dann hat ein 32 Bit Hash schon ab einer Datei von 5 Byte Größe garantiert im ersten Byte alle Kollisionen drin, da immer nur die letzten 4 Byte zählen. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Dazu wollte ich eine Struktur, die in der Lage ist das lokale und das remote Verzeichnis gleichermaßen abzubilden, um dies anzuzeigen und schnell abzugleichen und zu bearbeiten. Eigentlich sollte das schon sehr im Sinne eines FTP-Clients, oder TotalCommander oder ähnlich aussehen. Nur das meine App eben noch spezielle Operationen mit den Dateien machen muss, was ein FTP-Client nicht kann. Weil das aber eigentlich eine recht generelle Aufgabenstellung ist, so denke ich zumindest, wäre es doch sehr wahrscheinlich dass es dafür bereits ein existierendes, optimales Pattern oder vielleicht eine fertige Lösung gibt. Die Abbildung eines solchen Verzeichnissystems (Baum) zur synchronisierung ist doch prinzipiell auf allen OS, FAT/NTFS/APFS-Systemen und so weiter gleich, der einzige Unterschied sind Details wie Pfad-Delimiter, Zugriffsrechte, oder ähnlich. Das sollte man doch perfekt plattformübergreifend abbilden und verwalten können mit einer Klasse, zumindest meiner Meinung nach. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Ok, ich fange an zu verstehen worauf Du hinaus möchtest und in meinem Kopf fängt ein Bild für die von Dir gewünschte Anwendung an zu entstehen. Aber wie ich das sinnvoll und anwenderfreundlich umsetzen sollte, keine Ahnung. Aber da ich heute schon ein paarmal diverse KIs "gequält" habe, dachte ich mir, versuchen wir es nochmal, aber ehrlich gesagt hab' ich keine Ahnung, ob das Ergebnis für Dich brauchbare Ideen oder umsetzbare Lösungsansätze enthält.
Die bisher von mir umgesetzen Dublettenprüfungen resultieren letztlich immer in einer Ja/Nein-Entscheidung, bei der dann nur noch entschieden werden muss, welche der Dubletten gelöscht werden soll. Deine Aufgabenstellung dürfte weit darüber hinausgehen. Der von der KI genannten Komponenten von JAM-Software scheinen nur für die VCL zur Verfügung zu stehen. Für mobile Apps also unbrauchbar. Unterhaltung mit der Ki meiner Wahl: Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin // Initialisieren Sie den TJamShellTree JamShellTree1.Root := 'C:\'; // Wählen Sie den Startordner aus // Ereignisse behandeln JamShellTree1.OnDblClick := JamShellTree1DblClick; JamShellTree1.OnShellChange := JamShellTree1ShellChange; JamShellTree1.OnGetImageIndex := JamShellTree1GetImageIndex; end; procedure TForm1.JamShellTree1DblClick(Sender: TObject); begin // Hier können Sie auf den Doppelklick auf einen Ordner oder eine Datei reagieren ShowMessage('Doppelklick auf: ' + JamShellTree1.SelectedPath); end; procedure TForm1.JamShellTree1ShellChange(Sender: TObject); begin // Hier können Sie auf Änderungen im aktuellen Ordner reagieren end; procedure TForm1.JamShellTree1GetImageIndex(Sender: TObject; Node: TTreeNode); begin // Hier können Sie den Bildindex für jeden Knoten festlegen // Beispiel: Node.ImageIndex := 0; // Ordnersymbol end; Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
procedure TForm1.JamShellTree1GetImageIndex(Sender: TObject; Node: TTreeNode);
begin // Benutzerdefinierte Symbole festlegen if Node.Text = 'MeinOrdner' then Node.ImageIndex := 1 // Index des benutzerdefinierten Ordnersymbols else Node.ImageIndex := 0; // Standard-Ordnersymbol end; procedure TForm1.JamShellTree1ContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); begin // Benutzerdefiniertes Kontextmenü hinzufügen if JamShellTree1.Selected <> nil then begin // Hier können Sie Ihr Kontextmenü erstellen und Aktionen hinzufügen // Beispiel: ShowMessage('Kontextmenü für ' + JamShellTree1.Selected.Text); end; end; Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin // Initialisieren Sie den TJamShellTree JamShellTree1.Root := 'C:\'; // Wählen Sie den Startordner aus // Ereignisse behandeln JamShellTree1.OnDblClick := JamShellTree1DblClick; JamShellTree1.OnShellChange := JamShellTree1ShellChange; JamShellTree1.OnGetImageIndex := JamShellTree1GetImageIndex; JamShellTree1.OnContextPopup := JamShellTree1ContextPopup; end; procedure TForm1.JamShellTree1DblClick(Sender: TObject); begin // Hier können Sie auf den Doppelklick auf einen Ordner oder eine Datei reagieren ShowMessage('Doppelklick auf: ' + JamShellTree1.SelectedPath); end; procedure TForm1.JamShellTree1ShellChange(Sender: TObject); begin // Hier können Sie auf Änderungen im aktuellen Ordner reagieren end; procedure TForm1.JamShellTree1GetImageIndex(Sender: TObject; Node: TTreeNode); begin // Benutzerdefinierte Symbole festlegen if Node.Text = 'MeinOrdner' then Node.ImageIndex := 1 // Index des benutzerdefinierten Ordnersymbols else Node.ImageIndex := 0; // Standard-Ordnersymbol end; procedure TForm1.JamShellTree1ContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); begin // Benutzerdefiniertes Kontextmenü hinzufügen if JamShellTree1.Selected <> nil then begin // Hier können Sie Ihr Kontextmenü erstellen und Aktionen hinzufügen // Beispiel: ShowMessage('Kontextmenü für ' + JamShellTree1.Selected.Text); end; end; Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
type
TComponent = class public procedure Add(Component: TComponent); virtual; abstract; procedure Remove(Component: TComponent); virtual; abstract; procedure Display(depth: integer); virtual; abstract; end; TComposite = class(TComponent) private children: TList; name: string; public constructor Create(n: string); procedure Add(Component: TComponent); override; procedure Remove(Component: TComponent); override; procedure Display(depth: integer); override; end; TLeaf = class(TComponent) private name: string; public constructor Create(n: string); procedure Add(Component: TComponent); override; procedure Remove(Component: TComponent); override; procedure Display(depth: integer); override; end; Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
type
TComponent = class public procedure Add(Component: TComponent); virtual; abstract; procedure Remove(Component: TComponent); virtual; abstract; procedure Display(depth: integer); virtual; abstract; end; TComposite = class(TComponent) private children: TList; name: string; public constructor Create(n: string); procedure Add(Component: TComponent); override; procedure Remove(Component: TComponent); override; procedure Display(depth: integer); override; end; TLeaf = class(TComponent) private name: string; public constructor Create(n: string); procedure Add(Component: TComponent); override; procedure Remove(Component: TComponent); override; procedure Display(depth: integer); override; end; implementation constructor TComposite.Create(n: string); begin name := n; children := TList.Create; end; procedure TComposite.Add(Component: TComponent); begin children.Add(Component); end; procedure TComposite.Remove(Component: TComponent); begin children.Remove(Component); end; procedure TComposite.Display(depth: integer); var i: integer; begin writeln(StringOfChar('-', depth) + name); for i := 0 to children.Count - 1 do TComponent(children[i]).Display(depth + 2); end; constructor TLeaf.Create(n: string); begin name := n; end; procedure TLeaf.Add(Component: TComponent); begin writeln('Cannot add to a leaf'); end; procedure TLeaf.Remove(Component: TComponent); begin writeln('Cannot remove from a leaf'); end; procedure TLeaf.Display(depth: integer); begin writeln(StringOfChar('-', depth) + name); end; Zitat:
Zitat:
Zitat:
|
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Danke sehr für die Mühe.
Ich habe mal ein bischen darüber nachgedacht wellche Pattern in Frage kämen und habe hier ein grobes Konzept, was hoffentlich das was ich möchte gut abbildet. Die spezifischen Adapter erzeugen die Hashes systemabhängig und geben diese an den zentralen Controller weiter.
Delphi-Quellcode:
Alle Funktionalität um die verschiedenen Lokationen abzubilden, synchron zu halten bzw. separat zu bearbeiten wäre zentral in dem PathController gekapselt.
program PathManagement;
{$APPTYPE CONSOLE} uses System.SysUtils, System.Generics.Collections; type IPathInterface = interface function GetFormattedPath: string; end; TPathController = class private FAdapters: TList<IPathInterface>; public constructor Create; destructor Destroy; override; procedure Attach(Adapter: IPathInterface); procedure NotifyAdapters; end; TPathAdapter = class(TInterfacedObject, IPathInterface) protected FController: TPathController; FPath: string; // Jeder Adapter verwaltet nun seinen eigenen Pfad public constructor Create(Controller: TPathController; const Path: string); function GetFormattedPath: string; virtual; abstract; end; TPathAdapter_Windows = class(TPathAdapter) public function GetFormattedPath: string; override; end; TPathAdapter_Unix = class(TPathAdapter) public function GetFormattedPath: string; override; end; { TPathController Implementation } constructor TPathController.Create; begin inherited Create; FAdapters := TList<IPathInterface>.Create; end; destructor TPathController.Destroy; begin FAdapters.Free; inherited Destroy; end; procedure TPathController.Attach(Adapter: IPathInterface); begin FAdapters.Add(Adapter); end; procedure TPathController.NotifyAdapters; var Adapter: IPathInterface; begin for Adapter in FAdapters do WriteLn(Adapter.GetFormattedPath); end; { TPathAdapter Implementation } constructor TPathAdapter.Create(Controller: TPathController; const Path: string); begin inherited Create; FController := Controller; FPath := Path; FController.Attach(Self); end; { TPathAdapter_Windows Implementation } function TPathAdapter_Windows.GetFormattedPath: string; begin //! Nur als Beispiel womrum es geht, genau solche unnötigen Umkopierungen möchte ich durch Baum-Auflösung im PathController Vermeiden Result := StringReplace(FPath, '/', '\', [rfReplaceAll]); end; { TPathAdapter_Unix Implementation } function TPathAdapter_Unix.GetFormattedPath: string; begin //! Nur als Beispiel womrum es geht, genau solche unnötigen Umkopierungen möchte ich durch Baum-Auflösung im PathController Vermeiden Result := StringReplace(FPath, '\', '/', [rfReplaceAll]); end; var Controller: TPathController; WinAdapter, UnixAdapter: IPathInterface; begin try Controller := TPathController.Create; WinAdapter := TPathAdapter_Windows.Create(Controller, 'C:\Users\Example\LocalDocuments'); // hier hinter sollten sich die identischen Dokumente befinden UnixAdapter := TPathAdapter_Unix.Create(Controller, 'FTP://mytest.com/remote/Documents'); // Controller.NotifyAdapters; // Kann verschiedene Lokations-übergreifende Aktionen anstossen ReadLn; finally Controller.Free; end; end. Alle Zugriffe, aus Sicht einer einzelnen Lokation wären in den PathAdaptern und deren PathInterface gekapselt. Damit komme ich glaube ich weiter. Ich werde zuerst mal versuchen in dem PathControll mit einem TFdMemTable zu arbeiten, wenn das nicht reicht, dann kann ich immer noch auf Bäume und sonstiges umsteigen. Vielen Dank erstmal an alle. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Ist seit 2.0 die Standard Hashfunktion für Dictionary und Co |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
(Es gibt eine Datei mit Länge 0, 2 Dateien mit Länge 1... also insgesamt 2^n-1 voneinander verschiedene Dateien mit maximaler Länge n-1. Wenn der n Bit Hash für all diese Dateien zu keiner Kollision führt, dann spätestens dann, wenn du zwei voneinander verschiedene Dateien der Länge n auswertest.) |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Es war ein vereinfachtes Beispiel, wo die meisten Kollisionen in einen definierten Bereich verschoben wurden, an der Anzahl ändert sich nicht viel, selbst wenn er besser rechnet.
Schlecht rechnen: * wenn ein Hash bereits bei gleichgroßem Eingang zu viele Kollisionen aufweist, dann wird es noch schlimmer. (z.B. wenn er immer nur ungerade Hashs ausspuckt, dann hast'e bereits 50% Ausfall, also bei CRC32 so, als hätte er nur 31 Bit) * und dazu kommen noch garantierte die Kollisionen mit kürzeren Eingängen, bei CRC32 also 0, 1, 2 oder 3 Byte große Daten. Selbst bei einem Hash mit 512 Bit, gibt es garantiert enorm viele Kollisionen, spätestens bei mehr als 64 Byte. Bei 68 Byte hast du da auch schon durchschnittlich 4 Milliarden Kollisionen pro Hash, also insgesamt mindestens 2,4^173. Drum hatte sich mein SerarchSameFiles nicht nur auf Hashs verlassen, sondern bei gleichen Hashs auch nochmal die kompletten Dateien verglichen. Es kommt nur drauf an, wie gut der Hash rechnet und wir wahrscheinlich es ist, dass wirklich einer dieser Fälle getroffen wird. Bei reinen Textdateien/XML/JSON fällt schonmal Vielles weg und wenn auch noch was "sinnvolles" in den Dateien steht, nochmal ein enorm großer Anteil. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
Ich habe mal Spring.Hash und XXHASH verglichen (1.400 Dateien, 14,3 GB); Letzteres hat ja auch eine 64 Bit-Implementierung. Spring.Hash und HashXXH32 waren genau gleich schnell, HashXXH64 jedoch spürbar (20% - 25%) schneller. Erstaunlich. Das ASM bringt offenbar gar nichts. Hat denn das MD5 beim Dateivergleich überhaupt eine Existenzberechtigung? Es ist ja offenbar unfassbar langsam. |
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Zitat:
|
AW: Optimaler Hash-Algorithmus und Strategie für Dateivergleiche, Verzeichnisbaum
Das erscheint mir als ein schwieriges Argument. Das Bessere ist der Feind des Guten. Joey Lynch hat 2021 auf Github den Artikel
![]() Ich habe das bei mir mal nachgeprüft (wie Lynch empfiehlt) und Faktor 10 war noch untertrieben. xxHash ist augenscheinlich sehr etabliert und da sehe ich für MD5 - jedenfalls für mich - keine Berechtigung mehr. Ich meine, Faktor 10 bei einem ohnehin schon zeitaufwändigen Prozess ... ? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:58 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