Python 2.6+ str.format() и регулярные выражения

Использование str.format() - это новый стандарт форматирования строк в Python 2.6 и Python 3. У меня возникла проблема при использовании str.format() с регулярными выражениями.

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

Предполагая, что указанным доменом является delivery.com, мое регулярное выражение должно возвращать a.delivery.com, b.delivery.com, www.c.delivery.com... но он не должен возвращать xadelivery.com.

import re

str1 = "www.pizza.delivery.com"
str2 = "w.pizza.delivery.com"
str3 = "pizza.delivery.com"

if (re.match('^(w{3}\.)?([0-9A-Za-z-]+\.){1}delivery.com$', str1): print 'String 1 matches!'
if (re.match('^(w{3}\.)?([0-9A-Za-z-]+\.){1}delivery.com$', str2): print 'String 2 matches!'
if (re.match('^(w{3}\.)?([0-9A-Za-z-]+\.){1}delivery.com$', str3): print 'String 3 matches!'

Запуск этого должен дать результат:

String 1 matches!
String 3 matches!

Теперь проблема заключается в том, что я пытаюсь заменить delivery.com динамически с помощью str.format...

if (re.match('^(w{3}\.)?([0-9A-Za-z-]+\.){1}{domainName}$'.format(domainName = 'delivery.com'), str1): print 'String 1 matches!'

Кажется, что это сбой, потому что str.format() ожидает, что параметры {3} и {1} будут функциями. (Я предполагаю)

Я мог бы конкатенировать строку с помощью оператора +

'^(w{3}\.)?([0-9A-Za-z-]+\.){1}' + domainName + '$'

Вопрос сводится к тому, можно ли использовать str.format(), когда строка (обычно регулярное выражение) имеет в ней < {n}?

Ответ 1

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

>>> pat= '^(w{{3}}\.)?([0-9A-Za-z-]+\.){{1}}{domainName}$'.format(domainName = 'delivery.com')
>>> pat
'^(w{3}\\.)?([0-9A-Za-z-]+\\.){1}delivery.com$'
>>> re.match(pat, str1)

Кроме того, re.match соответствует в начале строки, вам не нужно ставить ^, если вы используете re.match, вам нужно ^, если вы используете re.search.

Обратите внимание, что {1} в regex довольно избыточно.

Ответ 2

Per документация, если вам нужен литерал { или }, чтобы выжить в операции форматирования, используйте {{ и }} в исходной строке.

'^(w{{3}}\.)?([0-9A-Za-z-]+\.){{1}}{domainName}$'.format(domainName = 'delivery.com')