我希望能够与glob格式字符串的列表,而不是实际的文件在文件系统中的模式匹配。有什么办法可以执行此操作,或将glob模式很容易地转换为正则表达式吗?

2014-12-31 00:00:00
问题评论:

为什么 hell 实际上是此 downvoted?

可以添加更多的内容和上下文请?作为期望输出的虚拟文件系统中

因此要为 pythonlist的文件的列表给 glob,而不是会从文件系统占用?

@ap︰ 是不是它看起来像第一次看到匹配不同级别的路径元素时稍微复杂一点。

darnit,我戴上我获得按 crapload 的工作与我的工作在:-(其中的时刻很抱歉,无法详细阐述很多时刻。Fnmatch 的问题是如果您使用a/b/*/f.txt将不只匹配a/b/c/f.txta/b/q/f.txt (两个用于匹配) 还a/b/c/d/f.txta/b/c/d/e/f.txt,它的行为类似 Mercurial * * glob。它看起来像 * * 不支持 fnmatch 或 glob,至少不直接。

回答:

好的艺术家复制;好的艺术家窃取.

我窃取了;)

fnmatch.translate相当于正则表达式. globs ?*.*分别。我不到调整了它。

import re

def glob2re(pat):
    """Translate a shell PATTERN to a regular expression.

    There is no way to quote meta-characters.
    """

    i, n = 0, len(pat)
    res = ''
    while i < n:
        c = pat[i]
        i = i+1
        if c == '*':
            #res = res + '.*'
            res = res + '[^/]*'
        elif c == '?':
            #res = res + '.'
            res = res + '[^/]'
        elif c == '[':
            j = i
            if j < n and pat[j] == '!':
                j = j+1
            if j < n and pat[j] == ']':
                j = j+1
            while j < n and pat[j] != ']':
                j = j+1
            if j >= n:
                res = res + '['
            else:
                stuff = pat[i:j].replace('','\')
                i = j+1
                if stuff[0] == '!':
                    stuff = '^' + stuff[1:]
                elif stuff[0] == '^':
                    stuff = '' + stuff
                res = '%s[%s]' % (res, stuff)
        else:
            res = res + re.escape(c)
    return res + '(?ms)'

此一 à la fnmatch.filterre.matchre.search的工作。

def glob_filter(names,pat):
    return (name for name in names if re.match(glob2re(pat),name))

Glob 模式和在此页中找到的字符串通过考试。

pat_dict = {
            'a/b/*/f.txt': ['a/b/c/f.txt', 'a/b/q/f.txt', 'a/b/c/d/f.txt','a/b/c/d/e/f.txt'],
            '/foo/bar/*': ['/foo/bar/baz', '/spam/eggs/baz', '/foo/bar/bar'],
            '/*/bar/b*': ['/foo/bar/baz', '/foo/bar/bar'],
            '/*/[be]*/b*': ['/foo/bar/baz', '/foo/bar/bar'],
            '/foo*/bar': ['/foolicious/spamfantastic/bar', '/foolicious/bar']

        }
for pat in pat_dict:
    print('pattern :	{}
strings :	{}'.format(pat,pat_dict[pat]))
    print('matched :	{}
'.format(list(glob_filter(pat_dict[pat],pat))))

非常深刻,谢谢 !

好的内幕消息 !是的翻译的模式,忽略路径分隔符是一个好主意。请注意,它不但是,处理os.sepos.altsep但它应该是容易的调整。

谢谢 @martijn-pieters

我通常只是使用正斜杠规范化路径在任何处理之前。

glob模块使用fnmatch模块各个路径元素.

这意味着路径拆分为目录名和文件名,如果目录名包含元字符 (包含任何字符[*?) 然后这些都是展开的以递归方式.

如果您有了简单的文件名的字符串的列表,则只需使用fnmatch.filter()函数就足够了︰

import fnmatch

matching = fnmatch.filter(filenames, pattern)

但如果它们包含完整路径,则需要做更多工作,因为生成的正则表达式不会考虑路径段 (通配符不排除分隔符也他们调整跨平台路径匹配)。

您可以构造出路径,并从简单的试图,然后匹配针对此模式︰

import fnmatch
import glob
import os.path
from itertools import product


# Cross-Python dictionary views on the keys 
if hasattr(dict, 'viewkeys'):
    # Python 2
    def _viewkeys(d):
        return d.viewkeys()
else:
    # Python 3
    def _viewkeys(d):
        return d.keys()


def _in_trie(trie, path):
    """Determine if path is completely in trie"""
    current = trie
    for elem in path:
        try:
            current = current[elem]
        except KeyError:
            return False
    return None in current


def find_matching_paths(paths, pattern):
    """Produce a list of paths that match the pattern.

    * paths is a list of strings representing filesystem paths
    * pattern is a glob pattern as supported by the fnmatch module

    """
    if os.altsep:  # normalise
        pattern = pattern.replace(os.altsep, os.sep)
    pattern = pattern.split(os.sep)

    # build a trie out of path elements; efficiently search on prefixes
    path_trie = {}
    for path in paths:
        if os.altsep:  # normalise
            path = path.replace(os.altsep, os.sep)
        _, path = os.path.splitdrive(path)
        elems = path.split(os.sep)
        current = path_trie
        for elem in elems:
            current = current.setdefault(elem, {})
        current.setdefault(None, None)  # sentinel

    matching = []

    current_level = [path_trie]
    for subpattern in pattern:
        if not glob.has_magic(subpattern):
            # plain element, element must be in the trie or there are
            # 0 matches
            if not any(subpattern in d for d in current_level):
                return []
            matching.append([subpattern])
            current_level = [d[subpattern] for d in current_level if subpattern in d]
        else:
            # match all next levels in the trie that match the pattern
            matched_names = fnmatch.filter({k for d in current_level for k in d}, subpattern)
            if not matched_names:
                # nothing found
                return []
            matching.append(matched_names)
            current_level = [d[n] for d in current_level for n in _viewkeys(d) & set(matched_names)]

    return [os.sep.join(p) for p in product(*matching)
            if _in_trie(path_trie, p)]

此长可以快速找到匹配项,使用路径上任意位置的 globs:

>>> paths = ['/foo/bar/baz', '/spam/eggs/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/foo/bar/*')
['/foo/bar/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/*/bar/b*')
['/foo/bar/baz', '/foo/bar/bar']
>>> find_matching_paths(paths, '/*/[be]*/b*')
['/foo/bar/baz', '/foo/bar/bar', '/spam/eggs/baz']

@Veedrac︰ 生成的图案不排除通配符的路径分隔符。因此/foo*/bar/foolicious/spamfantastic/bar以及/foolicious/bar等。

@Veedrac︰ 完成,感谢为反馈。:-)

都别提,我找到了。我想fnmatch模块。

哦等待-fnmatch 不处理路径名 segmentization...唉。

您可以提供的示例中, fnmatch不能处理您的案例吗?

@BhargavRao: glob.glob()将 patters 应用于路径元素 separetely

@MartijnPieters 是。但是,当您查看globcpython 实现,它们只拆分路径 sep 和递归地使用fnmatch因此使用fnmatch的问题是什么是我的问题。(我草拟的答案已经准备就绪,基于一些假设,只是等待澄清 OP)

天哪上同样的线路,为该回答完 Martijn,!没有看到您的答复 !:) 马上扔掉我的草稿

虽然可以直接使用fnmatch.fnmatch检查是否某一模式匹配文件名,或不,您还可以使用fnmatch.translate方法生成超出给定的fnmatch模式的正则表达式︰

>>> import fnmatch
>>> fnmatch.translate('*.txt')
'.*.txt(?ms)'

文档:

fnmatch.translate(pattern)

返回转换为正则表达式的外壳式模式。

在 Python 3.4 + 上您可以只使用PurePath.match .

pathlib.PurePath(path_string).match(pattern)

在 Python 3.3 或更早版本 (包括 2.x),得到来自 PyPI pathlib .

请注意,若要获得独立于平台的结果 (这取决于为什么运行此代码) 要明确说明PurePosixPathPureWindowsPath.

请输入您的翻译

Python glob but against a list of strings rather than the filesystem

确认取消