Outils pour utilisateurs

Outils du site


comparefichiersrep

Test de comparaison des fichiers de même nom entre 2 répertoires

Objectif

On a plusieurs fichiers de même nom dans 2 répertoires. On veut savoir s'ils ont un contenu identique.

  • On peut sélectionner certains fichiers seulement par des motifs de sélection/exclusion.
  • Et on teste les fichiers présents dans les 2 répertoires.

Le résultat est renvoyé sous forme d'une liste de: [nom_de_fichier, resultat], avec resultat:

  • 0: les 2 fichiers sont identiques
  • 1: les 2 fichiers sont différents
  • 2: on ne sait pas, à cause d'une erreur dans la lecture des contenus (droits insuffisants par exemple)
  • 3: le fichier sélectionné du 1er répertoire est absent du 2ème
  • 4: le fichier sélectionné du 2ème répertoire est absent du 1er

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)

Code proposé

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!

comparefichiersrep.txt · Dernière modification: 2010/04/11 12:54 de tyrtamos

Outils de la page