Подтвердить что ты не робот

Используйте 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

Но я думаю, что это не очень элегантно и качественно.

У кого-то есть лучшая идея?

4b9b3361

Ответ 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)