42 votes

Utilisez fnmatch.filter pour filtrer les fichiers par plus d'une extension possible.

Étant donné le morceau de code python suivant :

for root, dirs, files in os.walk(directory):
    for filename in fnmatch.filter(files, '*.png'):
        pass

Comment puis-je filtrer pour plus d'une extension ? Dans ce cas particulier, je veux obtenir tous les fichiers se terminant par *.png, *.gif, *.jpg ou *.jpeg.

Pour l'instant, j'ai trouvé

for root, dirs, files in os.walk(directory):
    for extension in ['jpg', 'jpeg', 'gif', 'png']:
        for filename in fnmatch.filter(files, '*.' + extension):
            pass

Mais je pense que ce n'est pas très élégant et performant.

Quelqu'un a une meilleure idée ?

55voto

eumiro Points 56644

Si vous n'avez besoin que de vérifier les extensions (c'est-à-dire pas d'autres caractères génériques), pourquoi n'utilisez-vous pas simplement les opérations de base sur les chaînes de caractères ?

for root, dirs, files in os.walk(directory):
    for filename in files:
        if filename.endswith(('.jpg', '.jpeg', '.gif', '.png')):
            pass

9voto

Sven Marnach Points 133943

Je pense que votre code est en fait très bien. Si vous voulez toucher chaque nom de fichier une seule fois, définissez votre propre fonction de filtrage :

def is_image_file(filename, extensions=['.jpg', '.jpeg', '.gif', '.png']):
    return any(filename.endswith(e) for e in extensions)

for root, dirs, files in os.walk(directory):
    for filename in filter(is_image_file, files):
        pass

6voto

Chris Doggett Points 9987

Je l'ai utilisé avec beaucoup de succès.

import fnmatch
import functools
import itertools
import os

# Remove the annotations if you're not on Python3
def find_files(dir_path: str=None, patterns: [str]=None) -> [str]:
    """
    Returns a generator yielding files matching the given patterns
    :type dir_path: str
    :type patterns: [str]
    :rtype : [str]
    :param dir_path: Directory to search for files/directories under. Defaults to current dir.
    :param patterns: Patterns of files to search for. Defaults to ["*"]. Example: ["*.json", "*.xml"]
    """
    path = dir_path or "."
    path_patterns = patterns or ["*"]

    for root_dir, dir_names, file_names in os.walk(path):
        filter_partial = functools.partial(fnmatch.filter, file_names)

        for file_name in itertools.chain(*map(filter_partial, path_patterns)):
            yield os.path.join(root_dir, file_name)

Exemples :

for f in find_files(test_directory):
    print(f)

rendements :

.\test.json
.\test.xml
.\test.ini
.\test_helpers.py
.\__init__.py

Test avec des modèles multiples :

for f in find_files(test_directory, ["*.xml", "*.json", "*.ini"]):
    print(f)

rendements :

.\test.json
.\test.xml
.\test.ini

6voto

user225312 Points 22699

Ce serait une meilleure solution, peut-être parce que vous n'appelez pas + à plusieurs reprises et en utilisant un tuple au lieu de list .

for root, dirs, files in os.walk(directory):
    for extension in ('*.jpg', '*.jpeg', '*.gif', '*.png'):
        for filename in fnmatch.filter(files, extension):
            pass

A tuple est préférable car vous n'allez pas modifier l'extension une fois que vous l'avez créée. Vous allez simplement les itérer.

4voto

Mark Nenadov Points 579

Ce n'est pas vraiment élégant non plus, mais ça marche :

for root, dirs, files in os.walk(directory):
    for filename in fnmatch.filter(files, '*.png') + fnmatch.filter(files, '*.jpg') + fnmatch.filter(files, '*.jpeg') + fnmatch.filter(files, '*.gif'):
        pass

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X