Rekursives Dateien vergleichen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Hallo Ich habe mir ein script gemacht, das zwei ordner verleicht .
Als erstes Vergleicht es alle Dateinamen, wenn die Namen stimmen liest es immer von den 2 Dateien mit gleichen namen den inhalt in bytes aus.
wenn die bytes auch stimmen giebt es eine meldung.

Mein Problem:
Es fuktioniert nur wenn es dateien im ordner hat.
ich wollte es rekursiv machen aber es ging nie.
Könnt ihr mir helfen?

Das ist der Code

Code: Alles auswählen

# -*- coding: utf-8 -*-
import os
import sys
import time

if sys.platform == 'win32':
    zeichen = "\\"

else:
    zeichen = "/"

ordner1 = str(input("Bitte ersten Ordner angeben: ") + zeichen)
ordner2 = str(input("Bitte zweiten Ordner angeben:") + zeichen)

start = time.time()

dateien1 = os.listdir(ordner1)
dateien2 = os.listdir(ordner2)

grösse1 = 0
grösse2 = 0

zähler = 0

for o in dateien1:
    grösse1 += os.path.getsize(ordner1 + o)

for o in dateien2:
    grösse2 += os.path.getsize(ordner2 + o)

grösse_1 = str(grösse1)
grösse_2 = str(grösse2)

if dateien1 == dateien2:
    for i in range(len(dateien1)):
        zähler += 1
        print(str(zähler) +  "/" + str(len(dateien1)) , end="\r")
        
        datei1 = open(ordner1 + dateien1[i], "rb")
        bytes1 = datei1.read()
        datei1.close()
        
        datei2 = open(ordner2 + dateien2[i], "rb")
        bytes2 = datei2.read()
        datei2.close()
        
        if bytes1 == bytes2:
            pass
        
        else:
            print("Die Ordner haben nicht den gleichen Inhalt!\nNamen gleich, Inhalt nicht.\nOrdner Nr.1 ist " + grösse_1 + " Bytes gross.\nOrdner Nr.2 ist " + grösse_2 + " Bytes gross.")
                
    print(zähler)
    print("Die Ordner haben den gleichen Inhalt!\nNamen und Inhalt sind gleich.\nBeide Ordner sind " + grösse_1 + " Bytes gross.")
    ende = time.time()
    print("Laufzeit: " + str(ende - start) + " Sekunden")

else:
    print("Die Ordner haben nicht den gleichen Inhalt!\nNamen sind nicht gleich\nOrdner Nr.1 ist " + grösse_1 + " Bytes gross.\nOrdner Nr.2 ist " + grösse_2 + " Bytes gross.")


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

Pfade setzt man nicht mit + zusammen. Dann braucht man auch nicht selbst den Pfadtrenner ermitteln, wobei zeichen ein schlechter Variablenname ist. Statt dessen gibt es pathlib.Path. da gibt es auch Methoden zum rekursiven Verzeichnisdurchlaufen.
Die Reihenfolge, mit der os.listdir die Dateinamen liefert ist nicht definiert, einfach die Listen auf Gleichheit zu prüfen reicht also nicht. Sets wären eine Lösung.
Wenn man mehrere Listen durchlaufen will, nimmt man zip statt eines Indexes.
Statt Zahlen per str in Strings zu verwandeln und mit + zusammenzustückeln nimm Stringformatierung.
Vor dem Dateiinhaltevergleich würde man wohl erst die Grösse prüfen.
Input liefert schon einen String.
Dass sowohl Ausgegeben wird, dass die Verzeichnisse verschieden sind, als auch, dass sie gleich sind, ist verwirrend.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Vielleicht sollte man sich auch vorher gründlicher in der Standardbibliothek umschauen – zum Beispiel das `filecmp`-Modul.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

sirius3, was meinst du damit?
Die Reihenfolge, mit der os.listdir die Dateinamen liefert ist nicht definiert, einfach die Listen auf Gleichheit zu prüfen reicht also nicht. Sets wären eine Lösung.
Wenn man mehrere Listen durchlaufen will, nimmt man zip statt eines Indexes.
:?: :roll:
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Er meint damit, dass die Listen zwar den gleichen Inhalt haben, aber nicht die gleiche Reihenfolge der Elemente darin. Wenn du liste dann mit "==" abgleichst, kommt es zu Fehlern.

Zur Veranschaulichung:

Code: Alles auswählen

dir1 = ["Paint", "Unterlagen", "Games"]
dir2 = ["Unterlagen", "Games", "Paint"]




if dir1 == dir2:
    print("Gleich")
else:
    print("Ungleich")
Ausgabe:
Ungleich

Wenn du stattdessen Sets nutzt, dann spielt die Reihenfolge keine Rolle

Beispiel:

Code: Alles auswählen

dir1 = {"Paint", "Unterlagen", "Games"}
dir2 = {"Unterlagen", "Games", "Paint"}




if dir1 == dir2:
    print("Gleich")
else:
    print("Ungleich")
Ausgabe:
Gleich


Außerdem solltest du wirklich noch mal ein Grundlagen Tutorial durchlaufen (nicht böse gemeint), denn so Sachen wie

Code: Alles auswählen

for i in range(len(dateien1)):
macht man in Python nicht, du kannst direkt durch die Listen iterieren

Code: Alles auswählen

for file in dateien1:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jankie: bei der for-Schleife braucht man sowohl dateien1 als auch dateien2:

Code: Alles auswählen

for dateiname1, dateiname2 in zip(dateien1, dateien2):
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Naja eigentlich macht das keinen Sinn, denn diese Schleife kommt ja erst wenn man getestet hat das `dateien1` und `dateien2` gleich sind, also wären in der Schleife auch `dateiname1` und `dateiname2` grundsätzlich immer gleich, sofern `dateien1` und `dateien2` Listen sind, die man vor dem Vergleichen sortiert hat. Wenn es Mengen sind, macht die Schleife auch keinen Sinn.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Fire Spike: Man will vielleicht auch nicht einfach so Dateien zum vergleichen komplett in den Speicher laden. Das kann bei allem was ein bisschen grösser ist (DVD-Images, Datenbanken, Datendateien von virtuellen Maschinen, Sicherungskopien, …) unhandlich werden. Und wenn man rekursiv etwas machen will, braucht man mindestens eine Funktion und Rekursion. Mal als Ansatz und völlig ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from filecmp import dircmp as DirCmp, cmp as compare_files
import os


def walk_results(dircmp, path=''):
    yield path, dircmp
    for sub_name, sub_dircmp in dircmp.subdirs.items():
        yield from walk_results(sub_dircmp, os.path.join(path, sub_name))


def compare_directories(base_path_a, base_path_b):
    for path, dircmp in walk_results(DirCmp(base_path_a, base_path_b)):
        if dircmp.common_funny or dircmp.diff_files:
            return False
        else:
            for filename in dircmp.common_files:
                if not compare_files(
                    os.path.join(base_path_a, path, filename),
                    os.path.join(base_path_b, path, filename),
                    shallow=False,
                ):
                    return False
    return True


def main():
    path_a = '...'
    path_b = '...'

    if compare_directories(path_a, path_b):
        print('Verzeichnisse haben den gleichen Inhalt.')
    else:
        print('Der Inhalt der Verzeichnisse ist nicht gleich.')


if __name__ == '__main__':
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Danke Blackjack aber es meldet immer dass die ordner gleich sind:

Code: Alles auswählen

#!/usr/bin/env python3
from filecmp import dircmp as DirCmp, cmp as compare_files
import os


def walk_results(dircmp, path=''):
    yield path, dircmp
    for sub_name, sub_dircmp in dircmp.subdirs.items():
        yield from walk_results(sub_dircmp, os.path.join(path, sub_name))


def compare_directories(base_path_a, base_path_b):
    for path, dircmp in walk_results(DirCmp(base_path_a, base_path_b)):
        if dircmp.common_funny or dircmp.diff_files:
            return False
        else:
            for filename in dircmp.common_files:
                print(filename)
                if not compare_files(
                    os.path.join(base_path_a, path, filename),
                    os.path.join(base_path_b, path, filename),
                    shallow=False,
                ):
                    return False
    return True


def main():
    path_a = '/media/usb'
    path_b = '/etc'

    if compare_directories(path_a, path_b):
        print('Verzeichnisse haben den gleichen Inhalt.')
    else:
        print('Der Inhalt der Verzeichnisse ist nicht gleich.')


if __name__ == '__main__':
    main()
output:

Code: Alles auswählen

Verzeichnisse haben den gleichen Inhalt.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Fire Spike: Dann wird wohl ein Fehler dein sein. Ich habe ja nicht umsonst „völlig ungetestet“ dazu geschrieben. 😎

Man muss die erste Bedingung in `compare_directories()` wohl mindestens noch um `left_only` und `right_only` erweitern. Ich hatte vermutet die wären auch in `diff_files` enthalten, sind sie aber nicht. Wie gesagt, das ist alles nicht ausgiebig getestet. Damit könntest Du ja anfangen: Unit-Tests schreiben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten