从Python的glob中提取所有扩展名

4

Python的glob模块允许指定通配符来列出文件,并且非常实用获取文件。

但是,如何获取/重构与通配符匹配的值?

例如,假设我有这些8个文件:fa1 fa2 fa3 fb1 fb3 fc1 fc2 fc3(注意:fb2 已经缺失)。

我可以执行

import glob
glob.glob('f[ab][12]') # ['fa2', 'fb1', 'fa1']

在这个例子中,我有两个通配符:[ab][12]。每个通配符都匹配值 ab12,但是只有 3 种组合的值可以进行匹配,因为一个文件 fb2(通配符的有效组合)不存在。
问题:如何获取每个通配符的有效匹配值列表? 更具体地说:如何获取元组的列表,其中包含与实际存在的文件匹配的(字符串)值?
在我的例子中,我想获取元组列表:[('a', '2'), ('b', '1'), ('a', '1')]
注意:
  1. 我不想获取完整的名称,只想获取由通配符匹配的值(在我的例子中,前缀'f'不属于通配符的一部分,因此我不想在元组列表中获取它);
  2. 这必须适用于所有支持的通配符,包括 * 和 ?。
我能想到的唯一解决方案是使用正则表达式,但这基本上意味着重新实现整个 glob 机制以提取中间数据。
编辑
由于我的问题被认为是“过于宽泛”,所以我重新制定了问题:是否可以使用 glob/fnmatch 模块而不是直接使用正则表达式来获得该结果?

我很好奇你为什么需要那些元组?也许有更好的方法来实现你想要做的事情。 - PM 2Ring
1
我必须对每个通配符匹配值的可能组合进行特定计算。我不需要“元组”,但我必须枚举由通配符匹配的任何可能值的组合。 - AkiRoss
2个回答

1
那些模块无法提供该信息。`glob` 调用 `fnmatch` 进行模式匹配,而 `fnmatch` 则使用正则表达式进行模式匹配。请参阅 Python 源代码中的 globfnmatch
这里是一些使用修改过的fnmatchtranslate函数的Python 2演示代码。根据我的简短测试,它看起来是有效的,但不保证:) 请注意,这忽略了fnmatch执行的其他操作,例如忽略大小写匹配。
#!/usr/bin/env python

import re, fnmatch, glob

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

    There is no way to quote meta-characters.
    Hacked to add capture groups
    """
    i, n = 0, len(pat)
    res = ''
    while i < n:
        c = pat[i]
        i = i+1
        if c == '*':
            res = res + '(.*)'
        elif c == '?':
            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 + '\Z(?ms)'


def test(shell_pat):
    print 'Shell pattern %r' % shell_pat
    names = glob.glob(shell_pat)
    print 'Found', names
    regex = pat_translate(shell_pat)
    print 'Regex %r' % regex
    pat = re.compile(regex)
    groups = [pat.match(name).groups() for name in names]
    print 'name, groups'
    for name, row in zip(names, groups):
        print name, row

“脏活”由translate完成,它通过将通配符转换为正则表达式实际上有很大帮助。一个想法是人为地用()包装通配符来创建组,但这有点棘手,因为使用了re.escape - AkiRoss
1
我认为我们不需要担心re.escape,因为它只用于转义非模式内容。我已经编写了一个简单的示例,很快就会将其添加到我的答案中。 - PM 2Ring
我在考虑以这种方式来“黑客化” translate:在通配符周围添加特殊序列(例如,my_file_* 将变成 my_file_BEGSTAR*ENDSTAR),让 translate 将其转换为正则表达式,然后用 () 替换序列以构建一个组,并重新编译正则表达式 my_file_(.*)。是的:序列可能会与字符串内容冲突,但可以避免。是的:不是很高效或干净,但可以重用旧代码。 - AkiRoss

1
在您的特定情况下,您可能希望使用 itertools.product
import itertools
import os


def get_wildcards(*specs):
    for wildcard in itertools.product(*specs):
        if os.path.exists('f{}{}'.format(*wildcard)):
            yield wildcard


for wildcard in get_wildcards('ab', '12'):
    print wildcard

输出:

('a', '1')
('a', '2')
('b', '1')

在这种情况下,您正在获取"ab""12"的“乘积”,最终得到4个元组,os.path.exists测试将消除那些没有指定现有文件的元组。

更新

计划将文件系统通配符转换为正则表达式(可以避免使用正则表达式,但会很痛苦)。接下来,我们将列出当前目录中的所有文件,将每个文件与正则表达式匹配。如果找到匹配项,则构造一个元组以产生该结果。
import re
import os


def regex_from_wildcard(wildcard):
    wildcard = wildcard.replace('.', r'\.')
    wildcard = wildcard.replace('[', '([').replace(']', '])')
    wildcard = wildcard.replace('?', r'(.)').replace('*', r'(.*)')
    wildcard = r'^{}$'.format(wildcard)
    wildcard = re.compile(wildcard)
    return wildcard


def generate_from_wildcards(wildcard):
    pattern = regex_from_wildcard(wildcard)
    for filename in os.listdir('.'):
        match_object = re.match(pattern, filename)
        if match_object:
            yield tuple(''.join(match_object.groups()))


# Test
for tup in generate_from_wildcards('f[bc]?'):
      print tup

一些注意事项:

  • 由于我仍不清楚您确切需要什么,因此解决方案可能在某些地方有所偏差
  • 如果通配符包含非通配符字符,例如f,点,则这些字符不包括在元组中。

?*通配符的支持怎么样? - AkiRoss
2
Hai Vu:问题中的模式只是一个例子,AkiRoss需要一个更通用的解决方案。 - PM 2Ring
请举例说明通配符。 - Hai Vu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接