Python:将格式字符串转换为正则表达式

7
我的应用程序的用户可以通过格式字符串配置某些文件的布局。
例如,用户指定的配置值可能是:
layout = '%(group)s/foo-%(locale)s/file.txt'

现在我需要找到所有已经存在的这样的文件。使用 glob 模块似乎很容易:

glob_pattern = layout % {'group': '*', 'locale': '*'}
glob.glob(glob_pattern)

然而,现在来了困难的部分:给定glob结果列表,我需要获取所有与给定占位符匹配的文件名部分,例如所有不同的“locale”值。
我想生成一个格式字符串的正则表达式,然后将其与glob结果列表进行匹配(或者跳过glob并自己进行所有匹配)。
但我找不到一种好的方法来创建正则表达式,既要捕获正确的组,又要转义输入的其余部分。
例如,这可能会给我一个匹配区域设置的正则表达式:
regex = layout % {'group': '.*', 'locale': (.*)}

为了确保正则表达式有效,我需要通过re.escape()将其传递,这样也会转义我刚插入的正则表达式语法。先调用re.escape()会破坏格式字符串。

我知道有fnmatch.translate(),它甚至可以给我一个正则表达式 - 但不会返回正确的组。

有没有一种好的方法可以做到这一点,而不需要像用一个正则表达式安全的唯一值替换占位符等hack操作?

可能有一些方法(第三方库?)可以以更灵活的方式解析格式字符串,例如在占位符位置分割字符串吗?

2个回答

2

由于您正在使用命名占位符,我建议使用命名组。以下是代码示例:

import re
UNIQ='_UNIQUE_STRING_'
class MarkPlaceholders(dict):
    def __getitem__(self, key):
        return UNIQ+('(?P<%s>.*?)'%key)+UNIQ

def format_to_re(format):
    parts = (format % MarkPlaceholders()).split(UNIQ)
    for i in range(0, len(parts), 2):
        parts[i] = re.escape(parts[i])
    return ''.join(parts)

然后进行测试:

>>> layout = '%(group)s/foo-%(locale)s/file.txt'
>>> print format_to_re(layout)
(?P<group>.*?)\/foo\-(?P<locale>.*?)\/file\.txt
>>> pattern = re.compile(format_to_re(layout))
>>> print pattern.match('something/foo-en-gb/file.txt').groupdict()
{'locale': 'en-gb', 'group': 'something'}

我原本希望找到一种不使用唯一标识符的方法,但这是对该方法的有趣变化。特别的,我喜欢我只需要一个唯一的分隔符,而不是每个需要匹配不同正则表达式的字段都需要一个唯一分隔符。 - miracle2k
如果唯一的分隔符让您感到过于担忧,您可以在其中包含一个数字,并逐个递增该数字,直到获得不在字符串中的内容。 - Duncan
好的,这适用于字符串,是否可能使其适用于更多结构? 例如将"node%(id)03d"解析为"node(?P<id>\d\d\d)" - Jens Timmerman

1
你可以尝试这个方法,它可以解决你的转义问题。
unique = '_UNIQUE_STRING_'
assert unique not in layout
regexp = re.escape(layout % {'group': unique, 'locale': unique}).replace(unique, '(.*)')

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