我们将如何在python中实现蚂蚁风格的模式集以选择文件组?

时间:2020-03-06 15:01:12  来源:igfitidea点击:

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

解决方案

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中," *"匹配任何内容(包括斜杠)。

是的。正如已经建议的,最好的选择是使用" os.walk"。或者,也许围绕" glob"和" fnmatch"模块编写包装器。

os.walk是我们最好的选择。我使用.svn进行了以下示例,因为我很方便,而且效果很好:

import re

for (dirpath, dirnames, filenames) in os.walk("."):
    if re.search(r'\.svn$', dirpath):
        for file in filenames:
            print file

一旦遇到**,就必须递归遍历整个目录结构,因此我认为此时最简单的方法是使用os.walk遍历目录,构造路径,然后检查它是否与模式匹配。我们可能可以通过以下方式将其转换为正则表达式:

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+"$")

(尽管请注意,这并不是功能齐全,它可能不支持[a-z]样式的glob模式,尽管可能会添加它)。 (第一个" \ * \ * /"匹配项将涵盖诸如" \ * \ * / CVS"匹配" ./CVS"之类的情况,以及仅在尾部匹配" \ * \ *"的情况。)

但是,很明显,当我们不处理**模式时,我们并不想遍历当前目录下的所有内容,因此,我认为我们需要一种两阶段的方法。我没有尝试实现下面的方法,可能有一些极端的情况,但是我认为它应该可以工作:

  • 在目录分隔符上拆分模式。即pat.split('/')-> ['**','CVS','*']
  • 遍历目录,并查看此级别模式的相关部分。 IE。 n层次深->看pat [n]
  • 用`glob \ _to \ _regex()转换成一个正则表达式
  • 以递归方式在当前目录中使用" os.walk",建立相对于我们开始的级别的路径。如果路径与正则表达式匹配,则产生它。
  • 如果pat不匹配" **",并且是模式中的最后一个元素,则产生所有匹配`glob.glob(os.path.join(curpath,pat [n]))'的文件/目录。
  • 如果pat不匹配" **",并且不是模式中的最后一个元素,则对于每个目录,检查它是否与pat [n]匹配(带有glob)。如果是这样,则向下递归,增加深度(因此它将查看" pat [n + 1]")

" waf"构建系统源代码中有一个实现。
http://code.google.com/p/waf/source/browse/trunk/waflib/Node.py?r=10755#471
可能这应该包装在自己的图书馆中吗?