É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?
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
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
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
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
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.
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)