在Python中查找"-regex ..."或如何查找整个名称(路径+名称)与正则表达式匹配的文件?

Dav*_* B. 10 python regex find

我想找到其全名(相对,虽然绝对也很好)的文件匹配给定的正则表达式(即,像glob模块一样,但是对于正则表达式匹配而不是shell通配符匹配).使用find,人们可以这样做,例如:

find . -regex ./foo/\w+/bar/[0-9]+-\w+.dat
Run Code Online (Sandbox Code Playgroud)

当然,我可以使用findvia os.system(...)或者os.exec*(...),但我正在寻找纯Python解决方案.以下代码os.walk(...)re模块正则表达式相结合是一个简单的Python解决方案.(它不健壮并且错过了很多(不那么角落)角落的情况,但是对于我的一次性目的来说已经足够了,为一次性数据库插入定位特定的数据文件.)

import os
import re

def find(regex, top='.'):
    matcher = re.compile(regex)
    for dirpath, dirnames, filenames in os.walk(top):
        for f in filenames:
            f = os.path.relpath(os.path.join(dirpath, f), top)
            if matcher.match(f):
                yield f

if __name__=="__main__":
    top = "."
    regex = "foo/\w+/bar/\d+-\w+.dat"
    for f in find(regex, top):
        print f
Run Code Online (Sandbox Code Playgroud)

但这效率低下.其内容与正则表达式不匹配的子树(例如,./foo/\w+/baz/继续上面的示例)是不必要地走的.理想情况下,这些子树应该从步行中删除; 不应遍历路径名不是正则表达式的部分匹配的任何子目录.(我猜想GNU find实现了这样的优化,但我还没有通过测试或源代码细读来证实这一点.)

有没有人知道基于强大的正则表达式的Python实现find,理想情况下是子树修剪优化?我希望我只是错过了os.path模块或某些第三方模块中的方法.

unu*_*tbu 6

来自help(os.walk):

当topdown为true时,调用者可以就地修改dirnames列表(例如,通过del或slice赋值),walk将只递归到名称保留在dirnames中的子目录中; 这可以用来修剪搜索...

因此,一旦dirnames确定子目录(列在其中)是不允许的,就应该从中删除它dirnames.这将产生您正在寻找的子树修剪.(请务必先从尾端查找del项目dirnames,这样就不会更改要删除的剩余项目的索引.)

import os
import re

def prune(regex,top='.'):
    sep=os.path.sep
    matcher = re.compile(regex)
    pieces=regex.split(sep)
    partial_matchers = map(
        re.compile,
        (sep.join(pieces[:i+1]) for i in range(len(pieces))))
    for root, dirs, files in os.walk(top,topdown=True):
        for i in reversed(range(len(dirs))):
            dirname=os.path.relpath(os.path.join(root,dirs[i]), top)
            dirlevel=dirname.count(sep)
            # print(dirname,dirlevel,sep.join(pieces[:dirlevel+1]))
            if not partial_matchers[dirlevel].match(dirname):
                print('pruning {0}'.format(
                    os.path.relpath(os.path.join(root,dirs[i]), top)))                
                del dirs[i]

        for filename in files:
            filename=os.path.relpath(os.path.join(root,filename))
            # print('checking {0}'.format(filename))
            if matcher.match(filename):
                print(filename)

if __name__=='__main__':
    prune(r'foo/\w+/bar/\d+-\w+.dat')
Run Code Online (Sandbox Code Playgroud)

使用如下目录结构运行脚本:

~/test% tree .
.
|-- foo
|   `-- baz
|       |-- bad
|       |   |-- bad1.txt
|       |   `-- badbad
|       |       `-- bad2.txt
|       `-- bar
|           |-- 1-good.dat
|           `-- 2-good.dat
`-- tmp
    |-- 000.png
    |-- 001.png
    `-- output.gif
Run Code Online (Sandbox Code Playgroud)

产量

pruning tmp
pruning foo/baz/bad
foo/baz/bar/2-good.dat
foo/baz/bar/1-good.dat
Run Code Online (Sandbox Code Playgroud)

如果取消注释"检查"打印语句,很明显修剪过的目录不会被移动.