Страницы

Поиск по вопросам

воскресенье, 29 декабря 2019 г.

Найти файлы по части пути, не только по имени c Python

#python #регулярные_выражения #python_3x #файлы #search


Как найти файлы полное имя которых заканчивается на '*/path/to/folder/myfile.ext'?

files = glob.glob('*/path/to/folder/myfile.ext')


Не находит. Работает только по короткому имени файла.

дополнительно
как в общем случае найти файл по регулярному выражению полного имени (пути)?
    


Ответы

Ответ 1



glob() уже работает с частями путей: import glob files = glob.glob('*/path/to/folder/myfile.ext') Эта команда найдёт все файлы вида: <текущая рабочая директория>/<любая поддиректория>/path/to/folder/myfile.ext Если хочется найти path/to/folder/myfile.ext в любом месте дерева директорий, то можно использовать ** шаблон: files = glob.glob('**/path/to/folder/myfile.ext', recursive=True) ** должен быть отдельным компонентом пути. В pathlib.Path есть .glob() и .rglob() методы соответственно: from pathlib import Path folder = Path("путь/к/папке/полностью") files_with_maps = folder.glob("имя.файл*.с.подстановочными.символами") как в общем случае найти файл по регулярному выражению полного имени (пути)? Файловые шаблоны и регулярные выражения это разные вещи, например, если *.txt шаблон преобразовать в regex: >>> import fnmatch, re >>> regex = fnmatch.translate('*.txt') # file pattern to regex >>> regex '.*\\.txt\\Z(?ms)' glob() работает с шаблонами. Если хочется работать с регулярными выражениями, то самостоятельно придётся генерировать пути, например, используя os.walk() как уже показал @Timofey Bondarev: import os import re def find_file(topdir, regex): for root, dirs, files in os.walk(topdir): for basename in files: path = os.path.join(root, basename) if re.fullmatch(regex, path[len(topdir):]): return path len(topdir): убирает родительскую директорию из пути (вложенные каталоги с / начинаются для целей regex). В данном случае, если ОСь не различает регистры для файловых имён, то регулярное выражение должно это учитывать самостоятельно (glob() автоматически это учитывает). Можно заметно улучшить производительность, если отсеивать директории (dirs можно по месту изменять) -- это требует введения ограничений на входные реглярные выражения, чтобы было ясно какая часть к путям директорий относится.

Ответ 2



Если я правильно понял задачу, то вот код, который может помочь: import os from itertools import chain from fnmatch import fnmatch def get_match_filenames(root, pattern): for path, dirs, files in os.walk(root): for filename in chain(files, dirs): full_name = os.path.join(path, filename) if fnmatch(full_name, pattern): yield full_name Здесь с помощью функции os.walk обходятся все файлы, находящиеся в каталоге root, подходящие под wildcard, сохранённый в pattern. Проверка на то, подходит ли имя файла под pattern, выполняется с помощью функции fnmatch встроенного модуля fnmatch. Функция реализована в виде генератора, что позволяет экономить на создании списка и удобно при больших объёмах подходящих файлов. Если требуется находить именно файлы, можно использовать сокращённую версию: import os from fnmatch import fnmatch def get_match_filenames(root, pattern): for path, _, files in os.walk(root): for filename in files: full_name = os.path.join(path, filename) if fnmatch(full_name, pattern): yield full_name В документации к библиотеке fnmatch говорится также, что функция filter работает как код [n for n in names if fnmatch(n, pattern)], но реализована более эффективно, поэтому можно сравнить скорость работы первого варианта с такой реализацией: import os from itertools import chain import fnmatch def get_match_filenames(root, pattern): full_names = (os.path.join(path, filename) for path, dirs, files in os.walk(root) for filename in chain(files, dirs)) return fnmatch.filter(full_names, pattern) Важно заметить, что здесь возвращается список целиком, и это недостаток такого подхода: при большом количестве результатов их придётся все одновременно держать в памяти, даже если это не требуется в алгоритме работы. Пример использования print(list(get_match_filenames('/home/user/docs', '*/src/*.py'))) Обратите внимание, в этом коде учитывается вид разделителей каталогов: если передаётся pattern = r'code\*.py', то на linux ничего найдено не будет, так как для разделения каталогов используется символ /.

Комментариев нет:

Отправить комментарий