Как бы вы реализовали наборы шаблонов в стиле муравья в python для выбора групп файлов? - PullRequest
3 голосов
/ 02 октября 2008

Ant имеет хороший способ выбора групп файлов, наиболее удобно использовать ** для обозначения дерева каталогов. Э.Г.

**/CVS/*            # All files immediately under a CVS directory.
mydir/mysubdir/**   # All files recursively under mysubdir

Больше примеров можно увидеть здесь:

http://ant.apache.org/manual/dirtasks.html

Как бы вы реализовали это в python, чтобы вы могли сделать что-то вроде:

files = get_files("**/CVS/*")
for file in files:
    print file

=>
CVS/Repository
mydir/mysubdir/CVS/Entries
mydir/mysubdir/foo/bar/CVS/Entries

Ответы [ 6 ]

4 голосов
/ 15 мая 2012

Извините, это довольно долго после вашего ОП. Я только что выпустил пакет Python, который делает именно это - он называется Formic и доступен в PyPI Cheeseshop . С помощью Formic ваша проблема решается с помощью:

import formic
fileset = formic.FileSet(include="**/CVS/*", default_excludes=False)
for file_name in fileset.qualified_files():
    print file_name

Есть одна небольшая сложность: default_excludes. Formic, как и Ant, по умолчанию исключает каталоги CVS (так как сбор файлов из них для сборки является по большей части опасным), ответ по умолчанию на этот вопрос не приведет к файлам. Установка default_exclude = False отключает это поведение.

3 голосов
/ 02 октября 2008

Как только вы натолкнетесь на **, вам придется пройтись по всей структуре каталогов, поэтому я думаю, что в этот момент самый простой способ - это перебрать каталог с помощью os.walk, construct путь, а затем проверьте, соответствует ли он шаблону. Вероятно, вы можете преобразовать в регулярные выражения что-то вроде:

def glob_to_regex(pat, dirsep=os.sep):
    dirsep = re.escape(dirsep)
    print re.escape(pat)
    regex = (re.escape(pat).replace("\\*\\*"+dirsep,".*")
                           .replace("\\*\\*",".*")
                           .replace("\\*","[^%s]*" % dirsep)
                           .replace("\\?","[^%s]" % dirsep))
    return re.compile(regex+"$")

(хотя обратите внимание, что это не так полнофункционально - например, он не поддерживает шаблоны глобусов в стиле * 1005, хотя, возможно, это можно было бы добавить). (Первое совпадение \*\*/ должно охватывать такие случаи, как \*\*/CVS сопоставление ./CVS, а также наличие только \*\* для сопоставления в хвосте.)

Однако, очевидно, что вы не хотите проходить все ниже текущего dir, когда не обрабатываете паттерн **, поэтому я думаю, что вам понадобится двухфазный подход. Я не пробовал реализовать приведенное ниже, и, возможно, есть несколько угловых случаев, но я думаю, что это должно сработать:

  1. Разделить шаблон по разделителю каталогов. т.е. pat.split('/') -> ['**','CVS','*']

  2. Пройдите по каталогам и посмотрите на соответствующую часть шаблона для этого уровня. то есть. n levels deep -> look at pat[n].

  3. Если pat[n] == '**' перейти на указанную выше стратегию:

    • Восстановите шаблон с помощью dirsep.join(pat[n:])
    • Преобразовать в регулярное выражение с glob\_to\_regex()
    • Рекурсивно os.walk через текущий каталог, создавая путь относительно уровня, с которого вы начали. Если путь соответствует регулярному выражению, выведите его.
  4. Если pat не соответствует "**", и это последний элемент в шаблоне, то вывести все файлы / каталоги, соответствующие glob.glob(os.path.join(curpath,pat[n]))

  5. Если pat не соответствует "**", и это НЕ последний элемент в шаблоне, то для каждого каталога проверьте, соответствует ли он (с glob) pat[n]. Если это так, вернитесь вниз, увеличивая глубину (чтобы она выглядела как pat[n+1])

2 голосов
/ 02 октября 2008

os.walk твой друг. Посмотрите на пример в руководстве по Python (https://docs.python.org/2/library/os.html#os.walk) и попробуйте построить что-нибудь из этого.

Чтобы сопоставить "**/CVS/*" с именем файла, который вы получаете, вы можете сделать что-то вроде этого:

def match(pattern, filename):
    if pattern.startswith("**"):
        return fnmatch.fnmatch(file, pattern[1:])
    else:
        return fnmatch.fnmatch(file, pattern)

В fnmatch.fnmatch, "*" соответствует чему угодно (включая косую черту).

1 голос
/ 24 декабря 2010

В исходном коде системы сборки waf есть реализация. http://code.google.com/p/waf/source/browse/trunk/waflib/Node.py?r=10755#471 Может быть, это должно быть обернуто в собственной библиотеке?

0 голосов
/ 02 октября 2008

os.walk - ваш лучший выбор для этого. Я сделал приведенный ниже пример с .svn, потому что он мне пригодился, и он отлично работал:

import re

for (dirpath, dirnames, filenames) in os.walk("."):
    if re.search(r'\.svn$', dirpath):
        for file in filenames:
            print file
0 голосов
/ 02 октября 2008

Да. Лучше всего, как уже было предложено, работать с «os.walk». Или напишите обертки вокруг модулей ' glob ' и ' fnmatch ', возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...