IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez Python Discussion :

Chercher des fichiers dans une arborescence avec la version récursive de glob.glob


Sujet :

Contribuez Python

  1. #1
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut Chercher des fichiers dans une arborescence avec la version récursive de glob.glob
    Bonjour,

    J'ai déjà proposé ici du code pour chercher/trier/filtrer des fichiers avec plusieurs types de critères de recherche: https://www.developpez.net/forums/d1...aine-derniere/.

    Mais dans des cas plus simples, il y a dans les dernières versions de Python, à partir de la v3.5, une option "récursive" des fonctions du module glob qui permet des recherches très performantes et très simples à utiliser.

    Par rapport aux solutions que j'avais données, on se limite avec glob aux motifs de type "wildcard". Pour savoir comment ça marche, on regarde la doc du module "fnmatch", avec en plus quelques particularités dans la doc du module "glob".

    Par contre, comme ça utilise os.scandir, c'est très rapide!

    Pour trouver tous les fichiers, il suffirait que je prenne un motif comme "*". Mais, par exemple, je veux tous les fichiers "jpg" (motif de type "wildcard": "*.jpg") de mon répertoire images et de ses sous-répertoires:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    fichiers = glob(os.path.join(repertoire, "**", "*.jpg"), recursive=True)
    # affichage:
    for fichier in fichiers:
        print(fichier)
    print("nombre de fichiers trouvés:", len(fichiers))
    Il est difficile de faire plus simple, non? L'astuce repose, en plus de l'option "recursive=True", sur l'utilisation du sous-répertoire "**" qui veut dire "n'importe quel répertoire ou chemin de répertoires", y compris aucun.

    Si on veut ajouter plusieurs motifs, ce n'est pas très compliqué non plus:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    fichiers = []
    for motif in ["*.jpg", "*.jpeg"]:
        fichiers += glob(os.path.join(repertoire, "**", motif), recursive=True)
    # affichage:
    for fichier in fichiers:
        print(fichier)
    print("nombre de fichiers trouvés:", len(fichiers))
    Une fois obtenu la liste complète des fichiers avec leur chemin complet, on peut, bien entendu, les trier selon n'importe quel critère:

    Tri alphabétique:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    fichiers.sort(key=lambda v: os.path.basename(v))
    # affichage:
    for fichier in fichiers:
        print(fichier)
    On a bien ici le tri alphanumérique des noms de fichier, quelque soit le sous-répertoire dans lequel ils se trouvent.

    Tri selon des tailles de fichiers (de la plus grande taille à la plus petite):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    fichiers.sort(key=lambda v: os.path.getsize(v), reverse=True)
    # affichage:
    for fichier in fichiers:
        print(fichier, os.path.getsize(fichier))
    Tri selon les dates et heures de modification, la plus récente en 1er:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    fichiers.sort(key=lambda v: os.path.getmtime(v), reverse=True)
    # affichage:
    for fichier in fichiers:
        print(fichier, str(datetime.fromtimestamp(os.path.getmtime(fichier), tz=None)).split('.')[0])
    Vous voyez qu'on peut trier avec le flottant renvoyé par getmtime, mais qu'on peut afficher un temps plus lisible avec la formule datetime => "2019-02-24 10:12:00"

    Maintenant, si on veut filtrer, c'est à dire ne retenir qu'un sous-ensemble des fichiers trouvés, il vaut mieux utiliser la fonction "iglob" qui est un itérateur. Cette fonction renvoie les fichiers sélectionnés un par un.

    Exemple pour mes fichiers image "jpg":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    n = 0
    for fichier in iglob(os.path.join(repertoire, "**", "*.jpg"), recursive=True):
        n += 1
        print(fichier)
    print("nombre de fichiers trouvés:", n)
    Pour filtrer ces fichiers, il suffit d'ajouter un test pour savoir si on retient ou non le fichier proposé. Par exemple, je ne vais afficher que les noms de fichier qui commencent par "A" (attention: dans cet exemple, la casse intervient: "A" n'est pas "a"):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    n = 0
    for fichier in iglob(os.path.join(repertoire, "**", "*.jpg"), recursive=True):
        if os.path.basename(fichier).startswith("A"):
            n += 1
            print(fichier)
    print("nombre de fichiers trouvés:", n)
    On peut même neutraliser certains noms de sous-répertoire. Par exemple, je veux neutraliser le contenu du sous-répertoire appelé "test" (ne pas oublier d'ajouter le backslash à la fin):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    n = 0
    for fichier in iglob(os.path.join(repertoire, "**", "*.jpg"), recursive=True):
        if "test\\" not in fichier:
            n += 1
            print(fichier)
    print("nombre de fichiers trouvés:", n)
    Mais dans ce cas, on élimine bien tous les sous-répertoires nommés "test", mais glob les parcourt quand même complètement, ce qui est moins élégant que os.walk avec lequel on peut éliminer ces sous-répertoires de la recherche.

    etc... On peut faire encore plein d'autres choses avec ces fonctions du module glob...

    En contrepartie des grandes qualités de rapidité et de simplicité du glob récursif, je lui trouve tout de même un défaut: quand il ne peut pas rentrer dans un sous-répertoire, il ne dit rien (exception silencieuse). C'est peut-être confortable, mais c'est dommage: s'il s'agit d'un répertoire interdit de Windows (même pour les administrateurs), ça va, mais s'il s'agit d'un répertoire "normal" qui a une erreur, on devrait le savoir. J'aurais bien aimé un "callback" dans les arguments pour récupérer les erreurs quand on le veut.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Petit complément à mon précédent message.

    Le moteur de recherche de Google nous a donné de très mauvaises habitudes: on voudrait ici trouver un fichier dans une arborescence de répertoire, mais on ne se rappelle pas exactement de son nom!

    Bien sûr, en utilisant iglob du module glob, on peut toujours chercher les fichiers qui possèdent une certaine séquence de caractères.

    Par exemple, on cherche dans le répertoire d'installation de Python, les fichiers python dont le nom possède la séquence "nany":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        repertoire = r"E:\Programmes\Python37" # vous mettez le bon chemin chez vous!
        motif = "*.py"
        nomref = "nany"
     
        for fichier in iglob(os.path.join(repertoire, "**", motif), recursive=True):
            nom = os.path.splitext(os.path.basename(fichier))[0] # nom du fichier sans extension
            if nomref in nom:
                print(fichier)
    Et... il n'y en a pas! Pourtant, on est sûr que ça doit être à peu près ça.

    Alors, on va utiliser SequenceMatcher du module difflib, qui permet de calculer un ratio de proximité entre 2 mots (ratio=1.0: égalité; ratio=0.0: aucune proximité) selon l'algorithme de Ratcliff and Obershelp.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import os
    from glob import iglob
    from difflib import SequenceMatcher
     
    ##############################################################################
    def fichierproche(repertoire, motif, nomref, casse=False, ratio=0.6):
        """Recherche dans l'arborescence du répertoire, des fichiers sélectionnés 
             avec le motif wildcard, et dont le nom (sans l'extension) se 
             rapproche le plus du nom de référence nomref
           casse: si False, on fait les comparaisons en majuscule
        """
        nomref = nomref if casse else nomref.upper()
        fichiers = []
        for fichier in iglob(os.path.join(repertoire, "**", motif), recursive=True):
            nom = os.path.splitext(os.path.basename(fichier))[0] # nom du fichier sans extension
            nom = nom if casse else nom.upper()
            ratio2 = SequenceMatcher(None, nom, nomref).ratio() # calcul du ratio de proximité
            if ratio2>=ratio:
                fichiers.append([fichier, ratio2]) # enregistrement du fichier et de son ratio de proximité
        fichiers.sort(key=lambda v: v[1], reverse=True) # tri des fichiers par ordre dégressif des ratios
        return fichiers
    Cette fonction va renvoyer la liste des fichiers (avec leur chemin) dont le ratio calculé sera >= au ratio défini en argument (0.6 par défaut), et triés par ordre décroissant des ratios calculés.

    Application:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    repertoire = r"E:\Programmes\Python37"
    motif = "*.py"
    nomref = "nany"
     
    for fichier, ratio in fichierproche(repertoire, motif, nomref):
        print(fichier, ratio)
    Et là, on trouve "tabnanny.py" dans lequel nany avait 2 "n": on n'avait aucune chance de le trouver autrement.

    Pareil si on cherche avec "mater" pour trouver finalement "formatter.py" avec 2 "t"

    Ou avec "pyt" pour trouver finalement "pty.py"

    Voilà une manière supplémentaire de trouver les fichiers qu'on cherche! Et ceci très rapidement: dans la seconde pour une recherche parmi environ 8000 fichiers python (et plus de 20.000 fichiers non-python)!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/05/2014, 14h39
  2. [BATCH]script pour encoder des fichiers dans une arborescence
    Par ashgan44 dans le forum Scripts/Batch
    Réponses: 4
    Dernier message: 11/05/2009, 15h04
  3. Réponses: 1
    Dernier message: 09/12/2008, 12h23
  4. [batch file] detruire des fichiers dans une arborescence
    Par Biosox dans le forum Scripts/Batch
    Réponses: 3
    Dernier message: 03/06/2008, 10h10
  5. chercher un fichier dans une arborescence
    Par diamonds dans le forum Entrée/Sortie
    Réponses: 15
    Dernier message: 16/03/2007, 14h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo