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

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

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

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

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

Мы рассмотрим простой и узнаваемый пример: сопоставление телефонных номеров. Начнем с простого регулярного выражения ...

phone_re = re.compile(r'(\d{3})(\d{3})(\d{4})')

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

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

# area code (\d{3}) => (\d{3}|\(\d{3}\))
phone_re = re.compile(r'(\d{3}|\(\d{3}\))(\d{3})(\d{4})')

С этим расширением мы теперь можем сопоставить больше входных данных (см. Ниже).

inputs = ['7033217654','(703)3217654']

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

Обычно это происходит тогда, когда большинство разработчиков уклоняются от использования regex-land. Но не волнуйтесь! Regex-land - плодородная территория. Давайте познакомимся с некоторыми методами управления кодом, которые помогут нам приручить наше регулярное выражение, поскольку оно поддерживает больше шаблонов.

Подробный

Подробный режим - это универсальная опция регулярного выражения, которая позволяет нам использовать многострочные строки, пробелы и комментарии в нашем определении регулярного выражения. Это большая победа! Теперь наше регулярное выражение можно разбить на несколько строк, каждая из которых соответствует отдельному компоненту телефонного номера.

phone_re = re.compile(r'''
  (\d{3}|\(\d{3}\))  # area code
  (\d{3})            # first 3
  (\d{4})            # last 4
''', re.VERBOSE)

Теперь давайте расширим наше регулярное выражение для обработки разделителей, чтобы мы могли распознавать такие номера телефонов, как (703) -321–7654, 703.321.7654 и (703) 321 7654. Мы заключили наши группы интересов в захват групп, но мы ' Re не заинтересован в захвате наших разделителей, поэтому давайте поместим их в группу без захвата.

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

# spaces               \s+
# dots                  \.
# dashes                 -
# non-capturing group  (:?)
# optional                ?
# altogether  (:?\s+|-|\.)?
phone_re = re.compile(r'''
  (\d{3}|\(\d{3}\))  # area code
  (:?\s+|-|\.)?      # separator
  (\d{3})            # first 3
  (:?\s+|-|\.)?      # separator  
  (\d{4})            # last 4
''', re.VERBOSE)

Взгляните на наши поддерживаемые входные данные прямо сейчас!

inputs = [
  '7033217654',   '(703)3217654',   '(703) 321 7654',
  '703.321.7654', '(703)-321-7654', '703-321-7654'
  # et cetera permutations
]

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

Многоразовые регулярные выражения

В качестве последнего действия мы извлечем наше регулярное выражение-разделитель и вставим его в регулярное выражение телефона с помощью строковой интерполяции.

sep = '(:?\s+|-|\.)?' # separator
phone_re = re.compile(r'''
  (\d{3}|\(\d{3}\))  # area code
  {sep}              # separator
  (\d{3})            # first 3
  {sep}              # separator
  (\d{4})            # last 4
'''.format(sep=sep), re.VERBOSE)

Вуаля! Ясность. Без каких-либо уловок, которые мы здесь использовали, наше регулярное выражение могло бы вырасти примерно так:

(\d{3}|\(\d{3}\))(:?\s+|-|\.)?(\d{3})(:?\s+|-|\.)?(\d{4})

Вы выбираете между двумя….

Заключение

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

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

Спасибо за чтение!