Python: преобразовать строку формата в регулярное выражение - PullRequest
7 голосов
/ 16 апреля 2010

Пользователи моего приложения могут настроить макет определенных файлов через строку формата.

Например, значение конфигурации, указанное пользователем, может быть:

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

Теперь мне нужно найти все такие файлы, которые уже существуют. Это кажется достаточно простым, используя модуль glob :

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

Однако теперь наступает сложная часть: учитывая список глобальных результатов, мне нужно получить все те части имени файла, которые соответствуют заданному заполнителю, например все различные значения «локали».

Я подумал, что сгенерирую регулярное выражение для строки формата, которое затем смогу сопоставить со списком результатов глоба (или, возможно, пропустить глоб и выполнить все сопоставления самостоятельно).

Но я не могу найти хороший способ создать регулярное выражение как с правильными захватами группы, так и с экранированием остальной части ввода.

Например, это может дать мне регулярное выражение, соответствующее локали:

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

Но чтобы убедиться в правильности регулярного выражения, мне нужно пропустить его через re.escape (), который затем также избегает синтаксиса регулярного выражения, который я только что вставил. Вызов re.escape () сначала разрушает строку формата.

Я знаю, что есть fnmatch.translate (), который даже дал бы мне регулярное выражение - но не тот, который возвращает нужные группы.

Есть ли хороший способ сделать это без взлома, например, заменить заполнители уникальным значением, безопасным для регулярных выражений и т. Д .?

Возможно, есть какой-нибудь способ (возможно, сторонняя библиотека?), Который позволяет более гибко разбирать строку формата, например, разбивать строку в местах размещения?

Ответы [ 2 ]

2 голосов
/ 16 апреля 2010

Поскольку вы используете именованные заполнители, я бы использовал именованные группы. Это похоже на работу:

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'}
1 голос
/ 16 апреля 2010

Вы можете попробовать это; это помогает вам избежать проблем.

unique = '_UNIQUE_STRING_'
assert unique not in layout
regexp = re.escape(layout % {'group': unique, 'locale': unique}).replace(unique, '(.*)')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...