Учебник: Регулярные выражения (regular expressions)

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

  • Вы пишете программу, в которой обрабатываются номера телефонов, допустим в формате +7(ххх)ххх-хх-хх. Возможно их надо найти в тексте, а может быть — проверить корректность. На месте номеров могли бы быть номер банковской карты, IP-адрес, электронная почта, ФИО (в формате Петров А.Ю.), да и вообще что угодно.
  • В Microsoft Word при поиске и замене можно включить режим поддержки регулярных выражений поставив галочку напротив пункта «подстановочные знаки». Потом можно искать все то, что указано в первом пункте, но программу писать не требуется. И заменять можно. В LibreOffice/OpenOffice это тоже поддерживается.
  • Естественно, регулярные выражения поддерживаются во всех современных средах разработки — Qt Creator, Microsoft Visual Studio, NetBeans, IntelliJ IDEA и даже блокнотах — Notepad++, kate, gedit и др. Вы пишете код и решили что-то переименовать, да как-то особенно…

Остается научиться всем этим пользоваться. Значительную часть описанных ниже примеров можно проверить в том же Notepad++ или Microsoft Word. Для других (связанных с программированием) — можно использовать сервис regex101, он удобен не только для обучения, но и для реальной разработки.

Содержание:

  1. Теоретический раздел
    1. Одиночные символы
    2. Квантификация
    3. Группировка (подвыражения)
    4. Что есть еще?
  2. Практический раздел. Ссылки

1 Теоретический раздел

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

1.1 Одиночные символы

Символ «точка» (.) заменяет в регулярных выражениях любой символ. Так, например, если в тексте есть слова "порог" и "пирог" — то выражение "п.рог" будет удовлетворять обоим из них. Ниже приведен пример такого поиска в тектовом редакторе kate, остальные примеры будут даваться без скриншотов.
Учебник: Регулярные выражения (regular expressions)

Если же нас интересуют не все варианты замены символа — используется представление с квадратными скобками. В скобках перечисляются альтернативные символы. Также, в квадратных скобках можно задавать диапазоны символов с помощью «тире». Ниже приведена схема для выражения «var_[a-d][123]», можно попробовать выписать строки, которое оно описывает:
Учебник: Регулярные выражения (regular expressions)

Если символ «тире» должен являться частью перечисления — его нужно ставить первым или последним. Например, в таком выражении:
Учебник: Регулярные выражения (regular expressions)
ставить тире между "+" и "*" нельзя, так как это будет интерпретировано как диапазон.

Также с помощью перечислений можно искать «все символы кроме», для этого первым символом перечисления должен быть "^". Так, чтобы найти в тексте все символы кроме "ё", "й" и символов "a-z" можно использовать такое выражение: "[^ёйa-z]".

Если символ "^" стоит вне квадратных скобок — то он задает начало строки (до сих пор поиск осуществлялся во всем тексте). Символ "$" соответствует концу строки.

Если вдруг вам нужно найти в тексте какой-либо из «управляющих символов» — то его нужно экранировать с помощтю слеша. Так, например, символы "^", "[" в регулярном выражении должны быть заменены на "\^", "\[". На практике часто приходится искать символ слеша, который также является управляющим и заменяется на "\\".

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

ВыражениеСимвол
"\d"цифра
"\D"все кроме цифры "[^0-9]"
"\s"пробельный символ (табуляции, пробелы)
"\S"все, кроме пробельных символов
"\w"буква (любой язык, в любом регистре)
"\W"все кроме букв
"\b"граница слова
"\B"не граница слова

Такие обозначения могут использоваться в качестве элементов перечисления, например "[\d\w]" соответствует букве или цифре.

1.2 Квантификация

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

ВыражениеКоличество повторений
"*"0 или более раз
"+"1 или более раз
"?"0 или 1 раз
"{n}"точно n раз
"{n,m}"от n до m раз

С помощью кванторов мы можем описать, например строку, содержащую номер банковской карты:
Учебник: Регулярные выражения (regular expressions)

Под такое описание подойдут, например, строки "1234-1234-1234-1234" и "12345678 12345678".

1.3 группировка (подвыражения)

Выражение может состоять из подвыражений, заключенных в круглые скобки. Для программиста это очень важно, так как к подвыражению можно обратиться по индексу. Кроме того, подвыражения используются для задания альтернатив, которые можно перечислять с помощью вертикальной черты. Так, например, следующее выражение соответствует строкам «+7 902», «8(902)» и еще множеству вариантов:

Учебник: Регулярные выражения (regular expressions)

Тут "\(" используется для экранирования скобок. Подвыражения на практике применяются очень часто, но нам не всегда нужна возможность получить подстроку, соответствующую подвыражению. При выборе подстрок в коде вашей программы «лишние» подвыражения мешают, из-за них «съезжают» индексы, исправить ситуацию можно с использованием следующего синтаксиса: "(?:pattern)". Кроме того, такая форма записи более эффективна, т.к. сохраняет меньше данных.

Также, с группами связано так называемое «заглядывание вперед» — это нечасто применяемая на практике техника позволяет проверить соответствие подвыражению, не смещая позицию поиска и не запоминая найденное соответствие. Синтаксис используется следующий "(?=pattern)". Пусть дан следующий файл со списком языков программирования:

Basic структурный
Lua  процедурный
Prolog логический
С++ объектно-ориентированный
Lisp функциональный
Logo функциональный

По выражению
Учебник: Регулярные выражения (regular expressions)
мы ожидаемо получим три строки, однако что если, к уже найденному подвыражению требуется применить какие-то дополнительные «фильтры»? То есть, после этой проверки мы хотим еще раз проверить названия языков. Сделать это можно заменив "?:" на "?=".

Учебник: Регулярные выражения (regular expressions)

Теперь будут получены только две строки — Lua и Lisp, а второе подвыражение "(.*)" будет сопоставлено с типами соответствующих языков.

Негативное заглядывания вперед ищет несоответствие строки шаблону "(?!pattern)". Такое выражение выбирает подстроки, не соответствующие "pattern" без запоминания подстроки и не смещая текущую позицию поиска. Так, для рассмотренного выше примера, такой тип заглядывания вернет единственную строку с языком Logo. Первое подвыражение выберет строки с языками Basic, Prolog, С++ и Logo, а второе — оставит из них только те, чьи названия начинаются с символа "L".

1.4 Что есть еще?

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

Описанное выше должно одинаково работать в любой среде, поддерживающей регулярные выражения, однако в отдельных реализациях доступно больше возможностей или синтаксис выражений может незначительно отличаться. С помощью регулярных выражений можно искать строки в тексте, однако в каком регистре выполняется поиск? — ответ зависит от реализации. Управлять регистром можно с помощью модификаторов : "(?i)" включает чувствительность к регистру, а "(?-i)" — выключает ее. Существуют и другие модификаторы, но они используются реже. Работа модификаторов зависит от реализации. Некоторые реализации поддерживают также флаги, которыми также можно управлять регистром.

Ряд реализаций поддерживает очень удобный поиск по условию: "(?(?=если)то|иначе)". Нечто подобное позволяет реализовать «просмотр вперед». «Если» условие выполнится — будет выполнено сопоставление с «то», в противном случае — с «иначе». Сопоставление в данном случае создает группу, к которой можно обратиться по индексу из вашего кода.

2 Практический раздел. Ссылки

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

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

Учебник: Регулярные выражения (regular expressions)

Картинка взята из статьи «Поиск с помощью регулярных выражений может быть простым и быстрым«. В ней можно прочитать про различные реализации выражений, а также о том, как написать выражение так, чтобы оно работало быстрее. Кстати, так как выражение преобразуется в автомат, то зачастую его удобно визуализировать — для этого есть специальные сервисы, например. Для последнего выражения статьи будет построен такой автомат:

Учебник: Регулярные выражения (regular expressions)

Примеры использования регулярных выражений:

  • для валидации вводимых в поля данных: QValidator примеры использования. Ряд библиотек построения графического пользовательского интерфейса позволяют закреплять к полям ввода валидаторы, которые не позволяет ввести в формы некорректные данные. По приведенной выше ссылке можно найти валидацию номера банковской карты и номера телефона с помощью регулярных выражений библиотеки Qt. Аналогичные механизмы есть в других языках, например в Java для этого используется пакет javax.faces.validator.Validator;
  • для парсинга сайтов: Парсер сайта на Qt, использование QRegExp. В примере с сайта-галереи выбираются и скачиваются картинки заданных категорий;
  • для валидации данных, передаваемых в формате JSON ряд библиотек позволяет задавать схему. При этом для строковых полей могут быть заданы регулярные выражения. В качестве упражнения можно попробовать составить выражение для пароля — проверить что строка содержит символы в разном регистре и цифры.

В сообществе Программирование и алгоритмы можно посмотреть дополнительную литературу по теме. Книгу Гойвертса и Левитана рекомендую посмотреть особенно, так как в ней по-полочкам разобраны десятки примеров, причем с учетом специфики реализации регулярных выражений в конкретных языках программирования.

Комментарии 2

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *