On a plusieurs fichiers de même nom dans 2 répertoires. On veut savoir s'ils ont un contenu identique.
Le résultat est renvoyé sous forme d'une liste de: [nom_de_fichier, resultat], avec resultat:
On peut aussi avoir une exception en cas d'erreur d'entrée/sortie plus importante (l'un des répertoires n'existe pas ou droits insuffisants pour lire leur contenu)
Voilà le code proposé (il est auto-documenté).
Seule particularité: pour tenir compte des noms de fichier avec caractères accentués des Windows français, le tri des noms de fichiers est fait en fonction de l'ordre de classement français du dictionnaire. J'ai utilisé ici une version simplifiée de: http://www-clips.imag.fr/geta/gilles.serasset/tri-du-francais.html. Si on veut revenir au tri classique (rang des caractères de l'unicode), il suffit de supprimer le 'comp' des instructions 'xxxx.sort(comp)'. Mais dans ce cas, les majuscules et les minuscules ne seront pas rangées à la suite pour une même lettre. On peut d'ailleurs facilement modifier la chaine alpha de la fonction comp() pour l'adapter à ce qu'on veut.
#!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import division import os import fnmatch ############################################################################## def comparefic(nfc1, nfc2, lgbuf=32*1024): """Compare les 2 fichiers et renvoie True ou False selon que leur contenu binaire est identique ou non (la comparaison s'arrête à la 1ère différence). Déclenche une exception à gérer par l'appelant en cas d'erreur de lecture """ f1 = None f2 = None result = False try: if os.path.getsize(nfc1) == os.path.getsize(nfc2): f1 = open(nfc1, "rb") f2 = open(nfc2, "rb") while True: buf1 = f1.read(lgbuf) if len(buf1) == 0: result = True break buf2 = f2.read(lgbuf) if buf1 != buf2: break f1.close() f2.close() except: if f1 != None: f1.close() if f2 != None: f2.close() raise IOError return result ############################################################################## def okselect(nf, selections=["*"], exclusions=[""]): """renvoie si un nom correspond à un motif de selection, sans être interdit par un motif d'exclusion """ for selection in selections: if fnmatch.fnmatch(nf,selection): # on a trouve un motif de selection qui marche for exclusion in exclusions: if fnmatch.fnmatch(nf,exclusion): # mais un motif d'exclusion l'interdit return False return True # une selection marche sans exclusion: c'est ok return False # aucun motif de selection ne marche ############################################################################## def comp(v1,v2): """comparaison alphabétique des chaines de caractères v1 et v2 (unicode): => renvoie un entier <0 si v1<v2 (= v1 est avant v2), => renvoie 0 si v1==v2, => renvoie un entier >0 si v1>v2 (= v1 est après v2) """ alpha = u"aAàÀâÂåÅæÆbBcCçÇdDeEéÉèÈêÊëËfFgGhHiIîÎïÏjJkKlLmMnNñÑoOôÔœŒp" + \ u"PqQrRsStTuUùÙûÛüÜvVwWxXyYÿŸzZ" lgalpha = len(alpha) lg1 = len(v1) lg2 = len(v2) lg = min([lg1,lg2]) # lg=longueur du mot le plus court entre V1 et v2 for i in range(0,lg): i1 = alpha.find(v1[i]) if i1<0: # le caractère i de v1 n'est pas dans alpha i1 = ord(v1[i]) # la comparaison respectera donc l'ordre unicode i2 = alpha.find(v2[i]) if i2<0: # le caractère i de v2 n'est pas dans alpha i2 = ord(v2[i]) # la comparaison respectera donc l'ordre unicode if i1<i2: return -1 if i1>i2: return 1 # ici, les lg 1ers caractères sont ident.: le plus court est avant l'autre return lg1-lg2 ############################################################################## def listefics(rep, selections=["*"], exclusions=[""]): """renvoie la liste de tous les fichiers du répertoire rep correspondant à la sélection-exclusion """ try: entrees = os.listdir(rep) # rep doit avoir le bon encodage except: raise # redéclenche l'exception pour traitement par l'appelant entrees.sort(comp) nfics = [] for entree in entrees: if not os.path.isdir(os.path.join(rep, entree)): if okselect(entree, selections, exclusions): nfics.append(entree) return nfics ############################################################################## def comparefics(rep1, rep2, selections=["*"], exclusions=[""]): """ Compare les contenus des fichiers de rep1 qui sont aussi dans rep2 et renvoie: => 0 = contenus identiques => 1 = contenus différents => 2 = échec de la comparaison (droit de lecture insuffisant p. ex.) => 3 = le fichier sélectionné de rep1 est absent de rep2 => 4 = le fichier sélectioné de rep2 est absent de rep1 génère une exception si la lecture des répertoires échoue """ try: nf1 = listefics(rep1, selections, exclusions) nf2 = listefics(rep2, selections, exclusions) except: raise # redéclenche l'exception pour traitement par l'appelant R = [] for nf in nf1: print nf nfc1 = os.path.join(rep1, nf) if nf in nf2: nfc2 = os.path.join(rep2, nf) try: res = comparefic(nfc1, nfc2) if res: R.append([nf, 0]) else: R.append([nf, 1]) except: R.append([nf, 2]) else: R.append([nf, 3]) for nf in nf2: if nf not in nf1: R.append([nf, 4]) R.sort(comp, key=lambda v: v[0]) return R
Et voilà comment on utilise (comparaison des contenus des fichiers *.doc entre les 2 répertoires):
rep1 = r"C:\Users\tyrtamos\chemin1".decode('utf-8') rep2 = r"C:\Users\tyrtamos\chemin2".decode('utf-8') msg = [u"identiques", u"différents", u"échec", u"absent du 2ème rép.", u"absent du 1er rép."] try: R = comparefics(rep1, rep2, [u"*.doc"]) for nf, r in R: print nf, msg[r] except: print u"erreur d'entrée-sortie"
Ce qui affichera, par exemple:
fichier1.doc identiques fichier2.doc identiques fichier3.doc échec fichier4.doc différents fichier5.doc absent du 2ème rép. fichier6.doc absent du 1er rép.
Dans cet affichage, les résultats sont triés par nom de fichier (tri alphabétique français). Si on veut un tri par code résultat, il suffit d'ajouter avant le print:
R.sort(key=lambda v: v[1]) # tri selon r
Amusez-vous bien!