Как создать регулярное выражение из предопределенных блоков

Я хочу создать регулярное выражение на основе шаблона и набора предопределенных блоков и использовать string.Template для подстановки.

Например:

  • template: /data/${year}_${month}_${day}/${year}${month}${day}_${type}_${id}.dat
  • blocks:
    • day: (?P\d{2})
    • month: (?P\d{2})
    • year: (?P\d{4})
    • type: (?P[BDPCLNIYSQJ])
    • id: (?P\d{8})

>>> string.Template(template).safe_substitute(blocks)

/data/(?P\d{4})_(?P\d{2})_(?P\d{2})/(?P\d{4})(?P\d{2})(?P\d{2})_(?P[BDPCLNIYSQJ])_(?P\d{8}).dat

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

Я ищу способ исправления шаблона (до или после замены), способ обмануть повтор для повторения дубликатов или полный новый подход к проблеме.

2
nl ja de
Вы можете дать им другое имя, нет?
добавлено автор nhahtdh, источник
идея состоит в том, чтобы иметь возможность повторно использовать блоки, не меняя их
добавлено автор RogerFC, источник

4 ответы

Я не уверен в Python, но и поддержка PCRE и Perl (? (DEFINE) ...). Таким образом, вы можете использовать что-то вроде этого

(?x) 
(?(DEFINE)
    (?        (?&long_date) | (?&short_date))
    (?   (?&year) _ (?&month) _ (?&day) _ (?&type) _ (?&id))
    (?  (?&year) _ (?&month) _ (?&day))
    (?         \d{2})
    (?       \d{2})
    (?        \d{4})
    (?        [BDPCLNIYSQJ])
    (?          \d{8})
)
(?&date)

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

«условная группа» формы (? (DEFINE) ...) может использоваться для определения   группы (названные и пронумерованные), которые никогда не оцениваются в строке, но могут   можно назвать «подпрограммами» из других источников. По сути, ОПРЕДЕЛЕНИЕ   условие всегда неверно. В такой ситуации может быть только одна альтернатива   группа.

http://www.pcre.org/changelog.txt

2
добавлено
Казалось бы, это правильный способ сделать это, но, к сожалению, python re, похоже, не поддерживает такие конструкции.
добавлено автор RogerFC, источник

Избавьтесь от элементов имени? P. т.е.

day: (?P\d{2})

становится

day: (\d{2})

Я никогда не использовал функцию? P перед tbh

Ваша идея шаблона регулярного выражения хороша!

0
добавлено
ОК, примените шаблон дважды, я постараюсь и сделаю как ответ, этот блок комментариев затрудняет :)
добавлено автор Vorsprung, источник
Элементы? P называются (как в "(? P <день> \ d {2})"), так как я хотел бы получить доступ к сопоставленной группе по имени. поскольку нет априорной группировки, мне нужно использовать эти имена, чтобы знать, что я получаю.
добавлено автор RogerFC, источник

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

day='(?P<$dayname>\d{2})'
d=dict(dayname='day_start')
Template(day).safe_substitute(d)

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

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

Следуя советам друга, я нашел способ добиться желаемого результата.

Идея состоит в том, чтобы изменить строку шаблона, чтобы исключить дубликаты vars перед заменой блоков регулярных выражений. На самом деле он не удаляет дубликаты, а заменяет их ссылкой на первый с синтаксисом (? P = name). Таким образом вы вынуждаете содержимое быть одинаковым везде, где вы используете этот блок.

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

Чтобы преобразовать дубликаты, я использую следующую функцию:

>>> def remove_duplicate_blocks(template):
        regex = '\$\{([\w]+)\}'
        def alt_seen(matchobj):
            x = matchobj.group(1)
            if x not in seen and not seen_add(x): return '${%s}' % x
            else: return '(?P=%s)' % x
        seen = set()
        seen_add = seen.add
        return re.sub(regex, alt_seen, template)

который возвращает преобразованный шаблон без дубликатов и принуждает все подобные блоки иметь одинаковый контент.

Впоследствии это просто вопрос замены блоков

>>> unique_blocks_template = remove_duplicate_blocks(template)
>>> print unique_blocks_template
/data/${year}_${month}_${day}/(?P=year)(?P=month)(?P=day)_${type}_${id}.dat

>>> string.Template(unique_blocks_template).safe_substitute(blocks)
'/data/(?P\\d{4})_(?P\\d{2})_(?P\\d{2})/(?P=year)(?P=month)(?P=day)_(?P[BDPCLNIYSQJ])_(?P\\d{8}).dat'

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

0
добавлено
Python
Python
7 654 участник(ов)

Уютный чат для профессионалов, занимающихся поиском питоньих мудростей. Как не получить бан: https://t.me/ru_python/577926

Python beginners
Python beginners
4 449 участник(ов)

Вопросы про Python для чайников. Cпам и троллинг неприемлем. Не злоупотребляйте стикерами. Частозадаваемые вопросы: https://github.com/ru-python-beginners/faq/blob/master/README.md Статистика тут: https://grstats.me/chat/x4qym2k5uvfkr3al6at7

pro.python
pro.python
1 090 участник(ов)

Сообщество разработчиков под Python Создатель: @rodgelius

Rude Python
Rude Python
971 участник(ов)

Python без „девочек”, здесь матерятся и унижают Django. Not gay friendly. Правила: t.me/rudepython/114107 @rudepython | t.me/rudepython

rupython
rupython
509 участник(ов)

Группа создана с целью оперативного получения ответов на возникающие вопросы по разработке на яп python, смежные темы, а также человеческого общения. Приветствую!

Python-programming
Python-programming
266 участник(ов)

Чат группы вконтакте https://vk.com/python_community