Doppelte Dateien finden

Code-Stücke können hier veröffentlicht werden.
Antworten
Ernesto_Fernandez
User
Beiträge: 3
Registriert: Sonntag 26. März 2017, 14:22

Hallo zusammen.
Ich hoffe Ihr könnt mir weiter helfen.
Bin leider absoluter Neuling was Python oder sonstige Script- bzw. Programmiersprachen angeht.
Ich versuche schon seit Tagen doppelte Dateien mittels md5-hash zu ermitteln, diese dann in der Ausgabe zu sortieren und auszugeben.
Zum Programm:
Alle Werte werden in eine Liste bzw. in mehreren Listen abgespeichert.
Zuerst werden die Dateien mittels os.walk ermittelt und in eine Liste gespeichert. Danach werden diese Daten an eine md5 Funktion übergeben und auch in eine Liste abgespeichert. Jetzt habe ich zwei Listen die gleich Lang sind. (md5-hash > file)
Erster Listeneintrag md5 entspricht erstem Eintrag file usw.
Danach übergebe ich beide Listen einer (nennen wir sie) Sortierfunktion. Diese ermittelt die Stellen der md5-Liste die doppelt oder mehrfach sind und schreibt sie in einer neuen Liste. Die fiel-Liste wird gleichermaßen in eine neue Liste geschrieben. Das Problem besteht jetzt darin, das die Daten bzw. md5-hashes leider nicht sauber zusammengeführt bzw. vernünftig untereinander geschrieben werden.
(Gleiche md5-hash untereinander und dann die dazu gehörigen Files)
z.B.
8128e7364c4522307a6ab947c8da9998 /home/user/Dokumente/datei
d779bde21ba041798a8771cd6f566234 /home/user/datei
8128e7364c4522307a6ab947c8da9998 /home/user/Download/datei
8128e7364c4522307a6ab947c8da9998 /home/user/Download/datei2
d779bde21ba041798a8771cd6f566234 /home/user/Musik/datei

Es wäre super wenn ihr mir weiter helfen könnt.

Sinn des Programms solle es sein doppelt oder mehrfache Dateien zu finden und diese dann zu löschen (natürlich soll eine Datei erhalten bleiben).
Es wäre natürlich schön wenn dies Sinnvoll geschieht.
Beispiel:
Ich habe einen Ordner mit einem Album (mp3). Des weiteren findet das Programm eine gleich Datei auf der Festplatte die aber nicht in diesem Albumordner ist. Ich möchte natürlich nicht das dieser Albumordner auseinander gerissen wird, sondern nur die Datei die einzeln auf der Platte ist gelöscht wird.

Für Vorschläge und Anregungen wäre ich sehr Dankbar. Wie ich aber ober erwähnt habe bin ich Neuling. Wenn Ihr Codevorschläge bzw. Änderungen macht, wäre nett wenn ihr mir erklärt was im einzelnen in eurem Code passiert. Des weitern sind meine Englischkenntnisse mit rudimentär sehr wohlwollend umschreiben. Bin aber auf einem aufsteigendem Ast.

PS: Ja ich weiß, das es im Internet zu hauf solche Programme gibt. Das Programm soll nur zur Übung dienen.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import hashlib
import os
import sys

# Funktionen
def programminfo():
    print("Diese Programm dient zur festtellung von doppelten Dateien.\r\n"
          "Verwenden Sie das Programm wie folgt:\r\n"
          ". python daten_info.py /home/benutzer/zum_Ortner")

# Hashwerte bilden
# Noch zu erweitern(sha512), gegebenfals über Klassen realisieren
def hash_md5(path, blocksize=65536):
    afile = open(path, 'rb')
    hasher = hashlib.md5()
    buf = afile.read(blocksize)
    while len(buf) > 0:
        hasher.update(buf)
        buf = afile.read(blocksize)
    afile.close()
    return hasher.hexdigest()

# Sortieren der mehrfachen Dateien
def sortieren(md5, filedump):
    #Variablen in der funktion
    d_md5=[]
    d_file=[]
    for i in range(len(md5)):
        if md5.count(md5[i]) >1:
            d_md5.append(md5[i])
            d_file.append(filedump[i])
    return d_md5, d_file

def ausgabe_doppel(md5, file):
    for i in range(len(md5)):
        print("{0:<32} {1:>1}".format(md5[i],file[i]))


# MainProg

#zu prüfender ortner
try:
    testortner=sys.argv[1]
except:
    programminfo()
    sys.exit()
    #testortner = r"/home/user/Musik"  #Debug


#Globale variablen im MainProg
filedump = []
md5 = []

#def dateien_finden(testortner):
for root, dir, files in os.walk(testortner):
    for filename in files:
        if filename not in filedump:
            path = os.path.join(root, filename)
            filedump.append(path)

#bilden der hashwerte(testortner)
for i in range(len(filedump)):
   #print(hash_md5(filedump[i]), filedump[i])
   md5.append(hash_md5(filedump[i]))    #md5summen in md5 schreiben

d_md5, d_file=sortieren(md5,filedump)
ausgabe_doppel(d_md5,d_file)


Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ernesto_Fernandez: ob das System als Zeilenumbruch '\r\n' oder nur '\n' benutzt, weiß Python und wandelt '\n' in das passende Zeileendekonstrukt um, zumal der Pfad andeutet, dass es sich sowieso um ein System handelt, das nur '\n' erwartet.
In »hash_md5«: besser wäre es eine Endlosschleife mit break zu schreiben, anstatt die read-Zeile doppelt zu haben.
»sortieren« macht einiges, nur nicht das, was der Name suggeriert. Statt zwei Listen, die man immer parallel halten muß, bietet es sich an, eine Liste mit Tupeln zu nehmen, oder hier passender, ein Wörterbuch. Nackte excepts vermeiden, da man da wirklich jeden Fehler abfängt. In Zeile 47 erwartest Du aber nur einen IndexError. Die if-Abfrage in Zeile 60 wird niemals nicht erfüllt sein. for-Schleifen über den Index statt über die Elemente einer Liste (bei mehreren Listen hilft zip) sind ein Anti-Pattern und sollten nicht gemacht werden.
Die Kommentare sind zum Großteil überflüssig, weil sie nur offensichtliches beschreiben.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import sys
import hashlib
from collections import defaultdict

def programminfo():
    print("Diese Programm dient zur festtellung von doppelten Dateien.\n"
          "Verwenden Sie das Programm wie folgt:\n"
          ". python daten_info.py /home/benutzer/zum_Ortner")

def hash_md5(path, blocksize=65536):
    hasher = hashlib.md5()
    with open(path, 'rb') as data:
        while True:
            buf = data.read(blocksize)
            if not buf:
                break
            hasher.update(buf)
    return hasher.hexdigest()

def generate_hashes(filenames):
    hashes2filenames = defaultdict(list)
    for filename in filenames:
        hash = hash_md5(filename)
        hashes2filenames[hash].append(filename)
    return hashes2filenames

def print_duplicates(hashes2filenames):
    for hash, filenames in hashes2filenames.items():
        if len(filenames) > 1:
            for filename in filenames:
                print("{0:<32} {1:>1}".format(hash, filename))

def iter_all_files(path):
    for root, _, files in os.walk(path):
        for filename in files:
            yield os.path.join(root, filename)

def main():
    if len(sys.argv) != 2:
        programminfo()
        return
    path = sys.argv[1]
    hashes2filenames = generate_hashes(iter_all_files(path))
    print_duplicates(hashes2filenames)

if __name__ == '__main__':
    main()
Nach welchen Kriterien willst Du denn festlegen, dass Datei A in Ordner B besser aufgehoben ist als in C?
Ernesto_Fernandez
User
Beiträge: 3
Registriert: Sonntag 26. März 2017, 14:22

@Sirius3: Danke Erstmal für die Antwort. Habe mit mir etwas Nachsicht wenn ich dir ein paar (in deinen Augen) unsinnige Fragen stelle. Will das ganze ja vollständig verstehen.
Ich denke du meinst das unter Linux kein ‚\r\n‘ benötigt wird. Für Windows sieht die Sache doch anders aus,oder?

Deine umgeschriebene ‚hash_ma5‘ muss ich erst mal verstehen. Warum ist es besser eine Endlosschleife mit break zu schreiben?

Gut die Namensgebung ist ist nicht glücklich gewählt, das stimmt. Sitze an dem Programm aber auch schon längere Zeit, und irgendwann fällt das einem nicht mehr auf. Unter anderem was das Veröffentlichen des Codes eine Spontanidee. Dachte mir ich kann nur von und mit anderen lernen.

Das mit dem Wörterbuch ist mir auch schon in den Sinn gekommen, hatte aber keine Ideen wie ich das am Sinnvollsten umsetze. So wie ich das verstanden habe kann es ja immer nur einen Schlüssel geben. Und Ja jetzt wo du es sagst hätte ich da das Verzeichnis als Schlüssel verwenden können anstatt dem md5-hash.

„Nackte excepts vermeiden, da man da wirklich jeden Fehler abfängt“

Aus was für Gründen möchte ich nicht jeden Fehler abfangen?
Die if-Abfrage in Zeile 60 wird niemals nicht erfüllt sein. for-Schleifen über den Index statt über die Elemente einer Liste (bei mehreren Listen hilft zip) sind ein Anti-Pattern und sollten nicht gemacht werden.
Über diesem Text muss ich genauer nachdenken. Den Ausdruck „Anti-Pattern“ kannte ich bis gerade nicht. Kann das nur zu sagen „Lerne noch“ Schlechte Lösungsansätze werden mir bestimmt noch oft passieren. Kann wie schon gesagt dadurch nur Lernen. Trotzdem Danke für die Kritik!

Jetzt zu Deiner Frage.
Wenn Ordner B zusammenhängende Dateien erhält wie z.B ein Musikalbum enthält und eines der Lieder in noch einem anderen Ordner liegt, dann soll mich das Programm fragen welches der Dateien gelöscht werden soll. Am besten wäre es noch wenn die jeweiligen Ordnerstrukturen (Inhalte der einzelnen Ordner) aufgelistet werden um dann eine Endscheidung zu treffen. Löschen= Ja/Nein Welche Datei = x oder y. So mein erster Gedankengang.

Gruß Ernesto
Ernesto_Fernandez
User
Beiträge: 3
Registriert: Sonntag 26. März 2017, 14:22

Muss nochmal genau deinen Code durchgehen. Mir sind da einige Dinge noch unklar. Ich melde mich wieder. Muss Dr. Google wohl einiges befragen!
Danke Trotz allem.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1011
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Hab mal was ähnliches programmiert: https://pastebin.com/PrYWGxnW
.. und nochmal mit einem bytearray und memoryview erweitert.

Was Fehlt, ist die Fehlerbehandlung. Es kommt auch mal vor, dass auf eine Datei nicht zugegriffen werden kann. In diesem Fall würde die Funktion ergebnislos abbrechen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten