web-dev-qa-db-fra.com

Utilisez fnmatch.filter pour filtrer les fichiers par plusieurs extensions de fichier possibles

Étant donné le morceau suivant de code python:

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

Comment filtrer 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, je suis venu avec

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?

33
tyrondis

Si vous avez seulement besoin 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 chaîne de base?

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

Je pense que votre code est vraiment bien. Si vous souhaitez 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
8
Sven Marnach

J'utilise cela 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 plusieurs modèles:

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

rendements:

.\test.json
.\test.xml
.\test.ini
6
Chris Doggett

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
4
Mark Nenadov

Ce serait une meilleure façon, 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

Un Tuple est préférable car vous n'allez pas modifier l'extension une fois que vous les aurez créées. Vous utilisez simplement pour les parcourir.

4
user225312

Voici ce que j'utilise pour filtrer les fichiers dans les répertoires de journaux Apache. Ici j'exclus les erreurs flles

rep_filters = [now.strftime("%Y%m%d")]
def files_filter(liste_fic, filters = rep_filters):
    s = "(fic for fic in liste_fic if fic.find('error') < 0"
    for filter in filters:
        s += " and fic.find('%s') >=0 " % filter
    s += ")"
    return eval(s)
0
Salvatore DI DIO