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:
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.
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))
Si on veut ajouter plusieurs motifs, ce n'est pas très compliqué non plus:
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:
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))
Tri alphabétique:
On a bien ici le tri alphanumérique des noms de fichier, quelque soit le sous-répertoire dans lequel ils se trouvent.
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)
Tri selon des tailles de fichiers (de la plus grande taille à la plus petite):
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.getsize(v), reverse=True) # affichage: for fichier in fichiers: print(fichier, os.path.getsize(fichier))
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"
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])
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":
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 n = 0 for fichier in iglob(os.path.join(repertoire, "**", "*.jpg"), recursive=True): 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 os.path.basename(fichier).startswith("A"): 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.
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)
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.
Partager