【问题标题】:extracting all expansions from python's glob从python的glob中提取所有扩展
【发布时间】:2015-07-14 13:34:46
【问题描述】:

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

但是如何获取/重建通配符匹配的值?

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

我可以的

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

在这种情况下,我有 2 个通配符:[ab][12]。它们中的每一个都匹配值ab12,但是这些值的组合只出现了3 个,因为一个文件fb2(通配符的有效组合)不存在.

问题:如何获取每个通配符的有效匹配值列表?更准确地说:如何获取与实际存在的文件匹配的(字符串)值的元组列表?

在我的示例中,我想获取元组列表:[('a', '2'), ('b', '1'), ('a', '1')]

注意:

  1. 我不想获取全名,只获取通配符匹配的值(在我的示例中,前缀'f' 不是通配符的一部分,因此我不想在元组列表中获取它);
  2. 这必须适用于所有受支持的通配符,包括 * 和 ?。

我能想到的唯一解决方案是使用正则表达式,但这基本上意味着重新实现整个 glob 机制以提取中间数据。

编辑

由于“过于宽泛”的问题 (???) 我得到了一个接近的建议,我将问题重新表述为:是否可以使用 glob/fnmatch 模块而不是直接使用正则表达式来获得该结果?

【问题讨论】:

  • 我很好奇你为什么需要这些元组?也许有更好的方法来做你想做的事情。
  • 我必须对每个通配符中每个可能的匹配值组合进行特定的计算。我真的不需要“元组”,但我必须枚举通配符匹配的任何可能的值组合。

标签: python glob


【解决方案1】:

这些信息无法通过这些模块获得。 glob 调用 fnmatch 进行模式匹配,fnmatch 使用正则表达式进行模式匹配工作。请参阅 globfnmatch Python 源代码。


这里有一些 Python 2 演示代码,它使用 fnmatch 中的 translate 函数的修改版本。从我的简短测试来看,它似乎可以工作,但不能保证。 :) 请注意,这会忽略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
  • 我不认为我们需要担心re.escape,因为它只是用来逃避非模式的东西。我拼凑了一个简单的例子。我会尽快将其添加到我的答案中。
  • 我正在考虑以这种方式破解translate:在通配符周围添加特殊序列(例如,my_file_* 将变为my_file_BEGSTAR*ENDSTAR),翻译成正则表达式,然后替换带有() 的序列来构建一个组并重新编译正则表达式my_file_(.*)。是的:序列可能与字符串内容发生冲突,但可以避免。是的:不是很高效或干净,但会重用旧代码。
【解决方案2】:

在您的具体情况下,您可能希望使用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、点,则这些字符不包含在元组中。

【讨论】:

  • ?* 通配符的支持怎么样?
  • Hai Vu:问题中的模式只是一个例子,AkiRoss 需要一个更通用的解决方案。
  • 请给出通配符的例子
猜你喜欢
  • 2014-05-24
  • 1970-01-01
  • 2015-10-23
  • 1970-01-01
  • 2010-10-07
  • 2021-07-18
  • 2019-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多