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

7 投票
2 回答
6165 浏览
提问于 2025-04-15 21:42

我的应用程序用户可以通过格式字符串来配置某些文件的布局。

比如,用户指定的配置值可能是:

layout = '%(group)s/foo-%(locale)s/file.txt'

现在我需要找到所有已经存在的这样的文件。使用glob模块,这看起来很简单:

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

但是,接下来就难了:根据这些glob结果的列表,我需要找出所有与给定占位符匹配的文件名部分,比如所有不同的“locale”值。

我想我可以为格式字符串生成一个正则表达式,然后用它去匹配glob结果列表(或者干脆跳过glob,自己进行所有匹配)。

但我找不到一个好的方法来创建这个正则表达式,既能正确捕获分组,又能转义输入的其他部分。

例如,这可能会给我一个匹配locale的正则表达式:

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

但为了确保这个正则表达式是有效的,我需要通过re.escape()来处理它,这样就会把我刚插入的正则语法也转义掉。先调用re.escape()会破坏格式字符串。

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

有没有好的方法来做到这一点,而不是像用正则安全的唯一值替换占位符这样的hack?

是否有可能有某种方法(也许是第三方库?)可以更灵活地解析格式字符串,比如在占位符位置拆分字符串?

2 个回答

1

你可以试试这个方法,它可以解决你在转义时遇到的问题。

unique = '_UNIQUE_STRING_'
assert unique not in layout
regexp = re.escape(layout % {'group': unique, 'locale': unique}).replace(unique, '(.*)')
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'}

撰写回答