2010-04-16 10 views
7

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ответ

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'} 
+0

Я надеялся найти способ, кроме использования уникального идентификатора, но это интересная спина на таком подходе. В частности, мне нравится, что мне нужен только один уникальный разделитель, а не один для каждого поля, которое должно соответствовать другому регулярному выражению. – miracle2k

+0

Если уникальный разделитель вас слишком беспокоит, вы всегда можете включить в него число и увеличить число до тех пор, пока не получите что-то, что отсутствует в строке. – Duncan

+0

ok, это работает для строк, можно ли сделать эту работу для большего количества конструкций? как разбор "узел% (id) 03d" to "node (? P \ d \ d \ d)" –

1

Вы можете попробовать это; он работает вокруг ваших проблем с экранированием.

unique = '_UNIQUE_STRING_' 
assert unique not in layout 
regexp = re.escape(layout % {'group': unique, 'locale': unique}).replace(unique, '(.*)') 
Смежные вопросы