RECHERCHE DE DOUBLONS DANS DES DOSSIERS.

mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007 - 31 mai 2010 à 08:30
aera group Messages postés 382 Date d'inscription mercredi 23 août 2006 Statut Membre Dernière intervention 8 novembre 2010 - 22 juil. 2010 à 20:47
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/51821-recherche-de-doublons-dans-des-dossiers

aera group Messages postés 382 Date d'inscription mercredi 23 août 2006 Statut Membre Dernière intervention 8 novembre 2010 18
22 juil. 2010 à 20:47
Dans ce cas je vais créer un petit code avec la procédure de test pour voir si le problème est matériel (Win only). En effet c'est peu être du a Tk.

Je te dé-conseil l'utilisation de py 3.x pour des raisons évidente de compatibilité, et même de stabilité, il n'a pas assez de passif ... même s'il règle plein de problème entre autre l'unicode !
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
22 juil. 2010 à 14:33
Personnellement je préfère py3k il est mieux construit et plus logique dans sa globalité, mais il y a tellement de modules non-adaptés que ça en devient handicapant.
Ici par exemple j'aurai préféré utiliser WxPy.

je suis sur un autre projet alors j'ai un peu de mal à me remettre à celui-ci,
normalement :
self.d=threading.Thread(None,self.app.SearchDuplicate,None,[en])
self.d.start()
ceci appel en tant que thread (unique) la fonction pour rechercher les doublons, au sein de celle-ci :
threading.Thread(None,self.DefSize,None,()).start()
ce thread sert juste à définir la taille totale des fichiers doublons, si les processus suivants se termine avant ce sous-thread le résultats dans self.Info_Size_Str sera erroné.

En fait le question du nombre d'octets a utiliser est très importantes car elle influence directement la reconnaissance bien sûr, mais surtout la vitesse du programme.
Après pas mal d'essais je me suis stabilisé avec 200 octets et ça semble convenir, cela couplé avec la vérification directe de la date et de la taille permet d'éviter les erreurs.

Pour ce qui est du bug, je crois que c'est à cause de l'instabilité de Tkinter, comme je manipule pas mal de StringVar et autres de ce genre à la voléee, le module me sort parfois des erreurs et crash le programme.

Je suis effectivement capable de tester le prog sous windows, merci de ton aide.
aera group Messages postés 382 Date d'inscription mercredi 23 août 2006 Statut Membre Dernière intervention 8 novembre 2010 18
22 juil. 2010 à 12:17
Ah ok, bug CodeS-SourceS ou erreur de ma part, je n'ai pas eu d'alerte mail !

J'ai une question : il est bien le petit Python 3 000 ?

Effectivement impossible de passer en wx sous Py3K, ça devra attendre.

Pour revenir au bug, je n'ai pas vraiment encore étudier le code, mais quand tu dis "toute la fonction est le thread", ça veut dire quoi ? Que tu créer un thread par fichier ou un thread pour tous les fichiers ?

Avec 1000 octets tu devrais être plus sure pour la vérif.

Ton bug ressemble à un problème matériel. Je vais te créer si j'ai le temps un petit script pour vérifier tout ça. Mais avant il faut que je sache si tu es capable de tester le programme sous Windows ?
xyz33 Messages postés 3 Date d'inscription samedi 5 janvier 2013 Statut Membre Dernière intervention 5 janvier 2013
28 juin 2010 à 00:54
101 ans, j'exagère un peu mais comme mon premier ordinateur était un ZX81 (cf http://fr.wikipedia.org/wiki/ZX81) j'ai l'impression que cela fait un siècle, et puis passer du code binaire en hexa à python ça fait du chemein à parcourir...

Bon, ceci étant, dans ma mandriva, j'ai du du python 3.1.1, du 2.6.4 et du 2.4.5. Une commande "python" en console indique 2.6.4, donc faisons un peu de nettoyage en éliminant le 2.4.5.
Et puis en revenant à la console je teste à tout hazard "python3" qui me répond 3.1.1, alors je rejoue en ajoutant le nom du fichier, ce qui me donne quelques messages d'erreur concernant des .gif (pb connu même si les gif en question ne servent (pour le moment) à rien:-)), un message d'erreur sur "tkinter", ben oui il manque le paquetage en 3.1.1 ! Et puis le miracle se produit.

Un test rapide sur un répertoire donne quelques doublons "pas idiots" et j'en resterai là pour ce soir !
Bonne nuit et A+
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
27 juin 2010 à 22:10
P.S. : xyz33 -> si t'as des questions pour mieux comprendre le programme, j'y répondrai avec plaisir.
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
27 juin 2010 à 22:09
aera group : salut, j'adorerai une version avec wxPython, mais le code est en Py3K et WX n'est pas encore porté à ma connaissance.
L'ancien code faisait les vérification par groupe de 50 en threading, mais celui-ci n'a plus besoin, toute la fonction est le thread.
Malgré cela il y a encore un bug sur 3 lancements au-dessus de 5 000 fichiers.
On vérifie la taille des 100 premiers octets, mais depuis j'ai remarqué que ça n'est pas suffisant, je l'ai passé à 220 octets.
Effectivement la partie explorateur ne fonctionne pas avec Linux malheureusement.

xyz33 : salut, j'ai testé ce code avec la dernière mouture de Ubuntu et cela marche sans problème.
"Insister dans le monde windows" n'est pas du tout une nécessité en programmation.
Commence par vérifier si tu as bien python3.0 ou plus.
PS : comment on se sent à 101 ans ?

mouss11 : merci, content de voir que tu traines toujours près de mon code :p.
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
27 juin 2010 à 20:27
@xyz33 et attention à ta version de python, ce code est pour python v3.X, pas compatible avec v2.X
après faut regarder les quelques lignes qui pourraient être incompatible avec linux, doit pas y en avoir des masses, l'auteur à déjà modifié le problème de slash dans les chemins, voir os.sep
xyz33 Messages postés 3 Date d'inscription samedi 5 janvier 2013 Statut Membre Dernière intervention 5 janvier 2013
27 juin 2010 à 20:22
Bonjour,
je découvre python et clairement je ne comprends pas encore le code présenté. Mais le concept et l'approche me semblent intéressant et j'aime comprendre comment le logiciel que j'utilise fonctionne.
Encore faut il le faire fonctionner...Et le premier essai sous linux ne donne rien, mais la remarque sur la ligne 295 me laisse penser qu'il faut que j'insiste dans le monde Windows.

Si Aéra nous fournit une version + stable, + fun coté IHM et portable win-linux-MAC, je rajoute 3 étoiles.

D'avance merci et bon courage !
aera group Messages postés 382 Date d'inscription mercredi 23 août 2006 Statut Membre Dernière intervention 8 novembre 2010 18
27 juin 2010 à 19:32
Yep !

Ah ça faisait longtemps que j'avais pas vu un code aussi riche !!! Merci pour mon désespoir chronique :p !

Voila une source intéressante. Comment améliorer la vitesse de comparaison sans sacrifier la stabilité (qui a mon sens est la priorité).
L'idée d'utiliser des threads est bonne, mais je pense savoir où tu commets une erreur : Pour chaque fichier tu lances un ou plusieurs threads (je n'ai pas bien regarder j'avoue). Le problème c'est que tu bouffes toutes les ressources de ton pc (processeur et accessoirement mémoire vive). Be fait qu'il bug au bout d'un certain nombre de fichier, vivent simplement du fait que le pc est surexploité !!! À vérifier s'il s'agit bel et bien de ce problème et tenter de le résoudre ...

En ce qui consterne l'interface si j'ai un peu de temps et si tu le souhaite, je peux toujours essayer de t'en faire une en xwPython plus stable ...

L'idée de vérifier la taille et les 1000 premier octets est une bonne méthode. Je pense qu'il faut faire une distinction de cas :

taille = taille_du_fichier
if taille <= taille_limite:
methode_hashage()
else:
methode_premier_octets()

Ensuite l'interface doit proposer pour chaque fichier vérifier par la deuxième méthode d'être vérifier par la méthode de hashage qui donne la confirmation ou l'exception ....

Voila, je n'ai malheureusement pas pu tester ton code car mon pc est (encore) tombé en panne (merci nvidia) et est partit faire un petit stage germanique de remise à niveau.
Cela dis je vais bien sûre le tester et tenté d'y apporté des amélioration si nécessaire.
Bonne continuation et bon courage pour la suite. Merci pour ce joli petit code.

PS : ligne 295 : 'C:/Windows/explorer' => pas compatible avec Linux je pense :p
Cela dis, es réellement important ?
____
Aéra
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
5 juin 2010 à 16:17
Salut,
j'ai mis à jour finalement en utilisant ta méthode de hashage qui devrait suffire pour comparer.
J'ai ainsi pu enlever une bonne partie du code et améliorer les performances ainsi que diminuer les ressources.

J'en suis à 16 secondes pour 9 000 fichiers.
les groupes sont maintenant complets. Il n'y a plus trois fois les fichiers dans des groupes différents s'ils sont en triples.

Les seules erreurs qui restent sont des bugs de Tkinter (qui sont pas mal ennuyants encore !).

Encore merci pour ton aide.
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
1 juin 2010 à 19:50
Pour l'IHM je suis comme toi ! ça me gave ! lol

Effectivement je ne suis pas certain que ma méthode fonctionne dans tous les cas comme en hash.
Mais ne pas oublier que j'ai couplé avec la taille donc 2 critères.
Pour le hash, on peut peut-être décidé de l'appliquer pour les fichiers inférieur à 1Mo (c'est un exemple) et au dessus avec une méthode plus rapide. (ou pour certain type de fichier je ne sais pas)
J'aimerais bien trouvé une autre technique mais je ne vois pas.

J'ai modifié un poil le code pour pouvoir vérifier dans plusieurs dossiers différents, ce qui est plus pratique :
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os, os.path, stat

def afficheDoublon(listPath):
listExt = ['.jpg', '.png', '.gif', '.bmp']
dicoDupli = {}
for path in listPath: #boucle sur la liste de dossier
for root, dirs, files in os.walk(path):
for file in files:
if os.path.splitext(file)[1] in listExt:
fileStream = open(root+os.sep+file,'rb')
stat = os.stat(root+os.sep+file)
#création d'une clé avec taille du fichier + 100 premiers octets
#ici on peut remplacer par le hash mais c'est lent :
#hashKey = hash(fileStream .read())
hashKey = str(stat.st_size) + str(fileStream.read(100))
fileStream.close()
if hashKey in dicoDupli:
dicoDupli[hashKey].append(root+os.sep+file)
else:
dicoDupli[hashKey] = [root+os.sep+file]
nbDupli = 0
for d in dicoDupli.keys():
if len(dicoDupli[d]) > 1:
print(dicoDupli[d])
nbDupli+=1
print(nbDupli)

J'appelle ainsi autant de dossier que je veux, ici un vieux dossier de sauvegarde et mon dossier de photo actuel:

afficheDoublon( ["D:\AUTRES\ARCHIVE\Framakey_Backup\Data\Photos","D:\PICTURE"] )

"D:\AUTRES\ARCHIVE\Framakey_Backup\Data\Photos" = 3390 fichiers/132 dossiers (environ 2,42Go)
"D:\PICTURE" = 13023 fichiers/273 dossiers (environ 28,4Go de photos)

J'ai essayé avec hash puis 100octets+taille fichier.
Résultat 897 groupes dans les 2 cas, c'est plutôt assez concluant.
temps en hash : 41 secondes à chaque lancement (seulement 3 lancements)
temps méthode 2 : 5 secondes à chaque lancement (seulement 3 lancements)

Il me reste à vérifier le contenu des groupes
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
1 juin 2010 à 14:58
Merci Mouss11, pour ton visible intérêt :p.

La fonction os.walk semble effectivement plus efficace qu'une fonction récursive.

Autrement la solution que tu utilise à la place du hashage est celle que j'avais utilisée au début, mais je l'avais repoussée car avec les images je ne pense pas que les 100 premiers octets soient significatifs. Mais si ça peut augmenter à ce point la vitesse de traitement, il est peut-être plus intéressant de vérifier le hashage a posteriori.

Pour les groupes je suis d'accord, mon programme ne repère que deux par deux. Il serait facile de changer cela.

Enfin, l'interface graphique est statique, car je n'ai pas eu le courage de paramétrer un dynamisme. Tkinter est trop précis (et buggé), ça demanderai pas mal de code en plus.

Je suis en partiel en ce moment, du coup je ferai les modifications en fin de semaine.
Merci beaucoup pour ton aide.
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
31 mai 2010 à 23:41
En partant du principe que ces 3 images sont identiques :
D:\IMG\1\img1.gif
D:\IMG\2\img2.gif
D:\IMG\3\img3.gif
ton programme va créer 3 groupes de 2 alors qu'il n'y a qu'un seul groupe de 3
Après je ne sais pas si on doit dire que l'on a 1 duplicata, 2 ou 3 ???

en parallèle je me fait mon propre script de doublon, ça me permet de faire des vérifications et je comptais m'en faire un. C'est très pratique d'avoir ton code en parallèle.
Je travailles sur le fait que je veux les doublons aussi pour de gros fichiers,et effectivement le hash est très lent !! du coup je suis parti sur la lecture des 100 premiers octets + la taille du fichier, et déjà ça donne une bonne idée même si on ne peut pas être sûr qu'il soit identique comme avec du hash. Par contre en vitesse, ça n'a rien à voir.
En partant de ça je me dis, pourquoi pas rajouter un bouton pour chaque doublon qui vérifierai le hash, donc au cas par cas si besoin.

Je te propose ce code, il est très rapide quelque soit la taille des fichiers (testé sur 229Go avec environ 1700 fichiers et ça met 1 seconde), faut appeler la fonction afficheDoublon avec un chemin en paramètre et ça affiche des groupes de duplicatas puis le nombre de groupe (pour info je trouves 117 groupes contre 209 avec ton programme pour la même recherche d'où mes premières phrases de se commentaire):

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os, os.path, re, stat

def afficheDoublon(path):
listExt = ['.jpg', '.png', '.gif', '.bmp']
dicoDupli = {}
for root, dirs, files in os.walk(path):
for file in files:
if os.path.splitext(file)[1] in listExt:
fileStream = open(root+os.sep+file,'rb')
stat = os.stat(root+os.sep+file)
#création d'une clé avec taille du fichier + 100 premiers octets
#ici on peut remplacer par le hash mais c'est lent : hashKey = hash(fileStream .read())
hashKey = str(stat.st_size) + str(fileStream.read(100))
fileStream.close()
if hashKey in dicoDupli:
dicoDupli[hashKey].append(root+os.sep+file)
else:
dicoDupli[hashKey] = [root+os.sep+file]
nbDupli = 0
for d in dicoDupli.keys():
if len(dicoDupli[d]) > 1:
print(dicoDupli[d])
nbDupli+=1
print(nbDupli)

Voilà n'hésite pas à me dire ce que tu en penses pour améliorer tout ça et j'espère t'avoir donné une piste pour améliorer ton source.
A+.
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
31 mai 2010 à 20:41
J'ai oublié, ton interface n'est pas redimmensionnable et moi zé un tout petit écran 12" et ça ne rentre pas ! alors j'ai mis redimmensionnable mais les boutons du bas ne remontent pas. Bon pas bien méchant, j'imagine que ce n'est pas ta priorité l'IHM.

Sinon pour la fonction searchFile, tu utilises une fonction récursive, rien à redire, mais si ça t'intéresses il existe une fonction sympas : os.walk

tu pourrais remplacer ta fonction avec ce code :

listExt = ['.jpg', '.png', '.gif', '.bmp']
for root, dirs, files in os.walk(os.path.realpath(path)):
for file in files:
if os.path.splitext(file)[1] in listExt:
self.found.append(root+os.sep+file)

Bon j'en suis à comprendre comment tu trouves les doublons, c'est intéressant... A+
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
31 mai 2010 à 19:31
J'ai essayé ton code, ça marche pas mal ;) juste check.gif et checked.gif que je n'avais pas et qui a planté, donc ça vite corrigé. je tourne sous Python 3.1 comme toi donc OK, pas essayé <3.
Par contre tu affiches la taille des fichiers en octet et tu affiches Ko a coté. donc soit tu écris 1ko ou 1000o (octets) mais pas 1000ko sinon ça fait 1Mo. Enfin rien de bien méchant.
Sinon j'avais fait un copier/coller d'un dossier et il m'a bien trouvé les fichiers en double et c'est bien là l'essentiel.
Je suis en train de coder un petit script pour lister des fichiers donc c'est intéressant de voir comment tu fais et si jamais j'ai des choses à optimiser je te le ferais savoir.

PS : tu as mis les extensions et taille max en dur, il serait intéressant de pouvoir paramétrer ça.
PS : je continues de regarder ton code, je repasserai par là si besoin ;)
A+
Rano Its Messages postés 11 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 6 décembre 2008
31 mai 2010 à 10:02
Merci,
C'est fait :).
mouss11 Messages postés 43 Date d'inscription mercredi 22 mai 2002 Statut Membre Dernière intervention 29 octobre 2007
31 mai 2010 à 08:30
Salut,
j'ai pas encore essayé ton source mais premier constat :
utilise os.sep a la place de '//' pour concatener tes chemins, comme ca ton source marchera sur toutes les plateformes.
Rejoignez-nous