Erstes Python Programm -- Doppelte Dateien suchen

Code-Stücke können hier veröffentlicht werden.
xxy

Erstes Python Programm -- Doppelte Dateien suchen

Beitragvon xxy » Montag 24. Juli 2006, 13:03

Hallo zusammen,

bin neu hier im Forum und neu bei python....

Ich habe mir mit dem E-Book "A Byte of Python" (www.byteofpython.info) einen kleinen Einblick in python verschafft.

Mein erstes Übungprogramm sucht in einem Verzeichnis nach doppelten Dateien und gibt diese aus. (Doppelte im Sinn von gleichem Inhalt und nicht gleichem Dateinamen..)

Ich wüsste gerne was ihr dazu meint und hoffe, dass ihr mir ein paar Verbesserungsvorschläge geben könntet.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
#
"""
Dieses Programm sucht nach doppelten Dateien in einem bestimmtem Verzeichnis.

Zum erkennen, ob die Dateien gleich sind, wird md5sum -b benutzt
"""
import os, sys, commands

def search_doublettes(__dirname__):
   '''
   Sucht nach doppelten Dateien in einem Verzeichnis.
   
   Die doppelten Files werden in einem Dictionary zurückgegeben.
   '''

   # Nach Dateien suchen und deren md5 Hash speichern
   print 'Suche nach Dateien im Verzeichnis', __dirname__
   str_path = 'find ' +  __dirname__ + ' -type f -exec md5sum -b \{\} \;'
   lines = commands.getoutput(str_path).split('\n')
   print 'Suche abgeschlossen, starte Vergleich der Dateien.'
   
   datei_dict = {}    # Enthaelt die untersuchten Dateien
   antwort_dict = {}   # Enthaelt die doppelten Dateien
   
   for line in lines:   
      hash = line.split(' ', 1)
      hash[1] = hash[1].replace('\n', '').replace('*', '') # find fuegt dem Pfad ein * hinzu
      
      # Wenn Hash noch nicht geprueft wurde --> zu datei_dicht hinzu fuegen
      if datei_dict.has_key(hash[0]) == False:
         datei_dict[hash[0]] = hash[1]
      else:
      # Diesen Hash gibt es schon! --> doppelte Datei
         if antwort_dict.has_key(hash[0]):
            antwort_dict[hash[0]].append(hash[1])
         else:
            antwort_dict[hash[0]] = [hash[1], datei_dict[hash[0]]]
   
   return antwort_dict

if __name__ == "__main__":
   # Zu pruefendes Verzeichnis einlesen
   print 'Durchsucht ein Verzeichnis nach Doubletten (gleichen Dateien)\n'
   try:
      if os.path.isdir(sys.argv[1]) == True:
         __dirname__ = str(sys.argv[1])
      else: # Wenn der Parameter kein gültiges Verzeichnis war
         __dirname__ = str(raw_input('Geben Sie das zu durchsuchende Verzeichnis an: '))

   except: # Wenn kein Parameter übergeben wurde
      __dirname__ = str(raw_input('Geben Sie das zu durchsuchende Verzeichnis an: '))

   # Ueberpruefen, ob die Eingabe akzeptabel ist
   if os.path.exists(__dirname__) != True or os.path.isdir(__dirname__) != True:
      print 'Falsches Verzeichnis angegeben'
      sys.exit(1)
   
   # Wenn alles OK ist, Suche starten
   antwort_dict =  search_doublettes(__dirname__)
   
   # Files ausgeben
   if len(antwort_dict) > 0:
      print '\nFolgende Dateien sind gleich:'
      for key, files  in antwort_dict.items():
         print '\nHash:', key
         for datei in files:
            print '\t'+datei
   else:
      print '\nEs wurden keine doppelten Dateien gefunden.'


Und eine Ausgabe des Programms:

Code: Alles auswählen

fri@valhalla ~/Desktop $ ./search_doublettes.py ./
Durchsucht ein Verzeichnis nach Doubletten (gleichen Dateien)

Suche nach Dateien im Verzeichnis ./
Suche abgeschlossen, starte Vergleich der Dateien.

Folgende Dateien sind gleich:

Hash: 3034c746b8510aad871f0ec5dbae7817
        ./test_data/mesmerize_11.wav
        ./test_data/asdfasdf.wav

Hash: a753625175a456f23d56651064ab4a40
        ./test_data/123.MP3
        ./test_data/MANAU__6.MP3

Hash: 88f3f739fae32fbc3a5a3da68f782d94
        ./test_data/mesmerize_3 (Kopie).wav
        ./test_data/mesmerize_3 (noch eine Kopie).wav
        ./test_data/mesmerize_3.wav


gruss xxy
BlackJack

Beitragvon BlackJack » Montag 24. Juli 2006, 13:47

Doppelte Unterstriche vor und nach einem Namen sollten für Python-Interna vorbehalten bleiben. Dein `__dirname__` sollte also besser nur `dirname` heissen.

Am Syntax-Highlighting kann man erkennen, das `hash` der Name einer eingebauten Funktion ist. Diese Namen sollte man nach Möglichkeit nicht für eigene Objekte verwenden.

Du kämst mit einem Dictionary weniger aus, wenn Du einfach erstmal alles ins `antwort_dict` schreibst und da am Ende einfach alle Einträge entfernst wo der Wert die Länge 1 hat.

Einen Wahrheitswert mit `True` oder `False` zu vergleichen ist irgendwie doppelt gemoppelt.

Nie und nimmer nicht ``except`` ohne eine konkrete Ausnahme verwenden. Damit maskiert man sich Fehlermeldungen und wenn man irgendeinen Fehler macht, dann funktioniert das Programm nicht, oder nicht richtig, ohne das man irgendeine Rückmeldung bekommt.

Zuguterletzt die Vorgehensweise: Prüfsummen bilden ist hier nicht die optimale Lösung. So muss jede Datei komplett gelesen werden. Besser ist es erst einmal alle Dateinamen mit der dazugehörigen Dateigrösse zu ermitteln. Diese kann man dann nach Grösse sortieren. Dateien deren Grösse nur einmal vorkommt, braucht man schonmal gar nicht betrachten. Und bei denen mit gleicher Grösse braucht man die auch nur Blöckeweise miteinander vergleichen und kann abbrechen sobald ein Unterschied auftritt. Auf diese Weise muss man nur die Doubletten wirklich komplett lesen.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Montag 24. Juli 2006, 13:54

Ehm, du nutzt eigentlich fast nur externe Befehle, die es sowieso meist nur unter unixartigen Systemen gibt.

Jetzt mal schnell zusammengehackt, ohne Candy, dafür aber platformunabhängig:

Code: Alles auswählen

#!/usr/bin/env python
# -*- encoding: latin-1 -*-
import os, md5

def main():
    files_here = [entry for entry in os.listdir(os.getcwd()) if os.path.isfile(entry)]
    files_hash = {}
    for path in files_here:
        f = file(path, 'rb')
        sum = md5.new(f.read())
        f.close()
        try:
            files_hash[sum.hexdigest()].append(path)
        except KeyError:
            files_hash[sum.hexdigest()] = [path]
   
    dupes = [values for values in files_hash.values() if len(values) > 1]
    print 'Identical files:'
    print dupes

if __name__ == '__main__':
    main()


Edit (Leonidas): Fährt jetzt auch geradeaus... ähm, ist jetzt auch platformunabhängig.
Zuletzt geändert von Leonidas am Montag 24. Juli 2006, 14:52, insgesamt 1-mal geändert.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Beitragvon gerold » Montag 24. Juli 2006, 14:34

Leonidas hat geschrieben:

Code: Alles auswählen

        f = file(path, 'r')

Hi Leonidas!

Damit es wirklich plattformunabhängig ist:

Code: Alles auswählen

f = file(path, 'rb')

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Beitragvon BlackJack » Montag 24. Juli 2006, 21:22

Und wenn ich mir Zeile 9 so anschaue, dann sollte man das nicht auf Verzeichnisse mit CD- oder gar DVD-Images oder Satellitenbilder loslassen. ;-)
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Montag 24. Juli 2006, 21:25

BlackJack hat geschrieben:Und wenn ich mir Zeile 9 so anschaue, dann sollte man das nicht auf Verzeichnisse mit CD- oder gar DVD-Images oder Satellitenbilder loslassen. ;-)

Es war nur ein schneller Hack, um das Programm des OP ohne externe Befehle zu implementieren. Für was richtiges würde man erst die Dateigröße checken und Kommandozeilenoptionen hätte es auch verdient. Das war aber im Timeframe alles nicht mehr drin ;)
My god, it's full of CARs! | Leonidasvoice vs Modvoice
xxy

Beitragvon xxy » Mittwoch 26. Juli 2006, 15:01

hi zusammen,

vielen dank für die tipps.

ich werde das script einmal anpassen und das ergebnis wieder hier posten..

gruss und danke

xxy

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder