Detector de Archivos Duplicados
Descripción
En un conjunto grande de archivos es posible que haya más de una copia del mismo archivo, ya sea que estén almacenadados en diferentes directorios o que tengan distintos nombres. El objetivo de este ejercicio es lograr identificar archivos duplicados en el directorio photos.
Así es como debe funcionar:
- Para buscar los archivos del directorio que terminan con una extensión de imagen, usaremos la función
walk
, de la sección Walking directories
def walk(dirname, visit_func, *args):
for name in os.listdir(dirname):
path = os.path.join(dirname, name)
if os.path.isfile(path):
visit_func(path, *args)
else:
walk(path, visit_func, *args)
- Para generar un digest de los contenidos de cada archivo, usaremos la función
md5_digest
, de la sección Checking for equivalent files,
def md5_digest(filename):
data = open(filename, 'rb').read()
md5_hash = hashlib.md5()
md5_hash.update(data)
digest = md5_hash.hexdigest()
return digest
-
Usando el módulo shelve, creamos un objeto shelf en dónde almacenaremos cada digest asociado con una lista de rutas de los archivos que generen ese digest.
-
Finalmente, buscaremos en el shelf los digests que estén asociados a múltiples rutas de archivos. Si encontramos alguno, usaremos la función
same_contents
, de la sección Checking for equivalent files, para confirmar que los archivos contienen los mismos datos.
def same_contents(path1, path2):
data1 = open(path1, 'rb').read()
data2 = open(path2, 'rb').read()
return data1 == data2
Sugerencias
Crea primero las siguientes funciones sugeridas y luego juntalas todas para completar el ejercicio.
-
Para identificar los archivos de imagen, escribe una función llamada
is_image
la cual reciba una ruta de archivo y una lista de extensiones de archivo. Esta función debe devolverTrue
si la ruta del archivo termina con una de las extensiones de la lista. Consejo: usa la funciónos.path.splittext
- o pide a un asistente virtual que escriba esta función por ti. -
Escribe una función llamada
add_path
la cual tome como argumentos una ruta de archivo y un objeto shelf. Debe de usar la funciónmd5_digest
para calcular un digest de los contenidos del archivo. Luego debe actualizar el shelf, ya sea para crear un nuevo elemento que asocie el digest con con una nueva lista que contenga la ruta del archivo, o agregando la ruta del archivo a una lista previamente asociada. -
Escribe una función llamada
process_path
la cual tome una ruta de archivo, useis_image
para verificar si es un archivo de imagen, y use la funciónadd_path
para agregarla al shelf. -
Cuándo completes esas funciones, puedes usar el siguiente script para crear el shelf, buscar en el directorio “photos” y agregar las rutas de archivo al shelf, y luego verificar si hay más de un archivo con el mismmo digest.
db = shelve.open('photos/digests', 'n')
walk('photos', process_path)
for digest, paths in db.items():
if len(paths) > 1:
print(paths)
- Encontrarás que hay un par de archivos con el mismo digest. Usa
same_contents
para verificar si los archivos contienen los mismos datos.
Ejemplo de Ejecución:
Archivos duplicados:
['/content/drive/MyDrive/photos_tp/jan-2023/photo1.jpg', '/content/drive/MyDrive/photos_tp/mar-2023/photo2.jpg']
Contienen los mismos datos?
True
Solución
Mostrar solución
import os
import shelve
import hashlib
def walk(dirname, visit_func, *args):
for name in os.listdir(dirname):
path = os.path.join(dirname, name)
if os.path.isfile(path):
visit_func(path, *args)
else:
walk(path, visit_func, *args)
def md5_digest(filename):
data = open(filename, 'rb').read()
md5_hash = hashlib.md5()
md5_hash.update(data)
digest = md5_hash.hexdigest()
return digest
def same_contents(path1, path2):
data1 = open(path1, 'rb').read()
data2 = open(path2, 'rb').read()
return data1 == data2
def is_image(file_path, extensions):
if not os.path.isfile(file_path):
return False
root, ext = os.path.splitext(file_path)
print(root, '\t', ext)
return ext in extensions
def add_path(path, shelf):
digest = md5_digest(path)
if digest not in shelf:
shelf[digest] = [path]
else:
file_list = shelf[digest]
file_list.append(path)
shelf[digest] = file_list
def process_path(file_path, extensions, shelf):
if is_image(file_path, extensions):
add_path(file_path, shelf)
db = shelve.open('/content/drive/MyDrive/photos_tp/digests', 'n')
walk('/content/drive/MyDrive/photos_tp', process_path, ['.jpg', '.png'], db)
print()
for digest, paths in db.items():
if len(paths) > 1:
print('Archivos con el mismo digest:')
print(paths)
print('Contienen los mismos datos?')
print(same_contents(paths[0], paths[1]))
db.close()
Fuente del ejercicio: https://allendowney.github.io/ThinkPython/chap13.html#id2