我们将如何在python中实现蚂蚁风格的模式集以选择文件组?
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
可能这应该包装在自己的图书馆中吗?