Используйте fnmatch.filter для фильтрации файлов более чем одним возможным расширением файла
Учитывая следующий фрагмент кода python:
for root, dirs, files in os.walk(directory):
for filename in fnmatch.filter(files, '*.png'):
pass
Как фильтровать несколько расширений? В этом специальном случае я хочу получить все файлы, заканчивающиеся на *.png, *.gif, *.jpg или *.jpeg.
На данный момент я придумал
for root, dirs, files in os.walk(directory):
for extension in ['jpg', 'jpeg', 'gif', 'png']:
for filename in fnmatch.filter(files, '*.' + extension):
pass
Но я думаю, что это не очень элегантно и качественно.
У кого-то есть лучшая идея?
Ответы
Ответ 1
Если вам нужно только проверить расширения (т.е. никаких дополнительных подстановочных знаков), почему бы вам просто не использовать основные операции с строкой?
for root, dirs, files in os.walk(directory):
for filename in files:
if filename.endswith(('.jpg', '.jpeg', '.gif', '.png')):
pass
Ответ 2
Я думаю, что ваш код на самом деле прекрасен. Если вы хотите коснуться каждого имени файла только один раз, определите свою собственную функцию фильтрации:
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
Ответ 3
Я использовал это с большим успехом.
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)
Примеры:
for f in find_files(test_directory):
print(f)
дает:
.\test.json
.\test.xml
.\test.ini
.\test_helpers.py
.\__init__.py
Тестирование с несколькими шаблонами:
for f in find_files(test_directory, ["*.xml", "*.json", "*.ini"]):
print(f)
дает:
.\test.json
.\test.xml
.\test.ini
Ответ 4
Это тоже не очень изящно, но он работает:
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
Ответ 5
Это было бы лучше, возможно, потому, что вы не вызываете +
несколько раз и используете tuple
вместо 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
лучше, потому что вы не собираетесь изменять расширение после его создания. Вы просто используете, чтобы перебирать их.
Ответ 6
Вот что я использую для фильтрации файлов в каталогах журналов apache.
Здесь я исключаю ошибки 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)