Шаблоны поиска

Приведём несколько примеров регулярных выражений.

В регулярных выражениях алфавитно-цифровые символы обычно обозначают сами себя. Например, шаблон Hello указывает искать символ H, за которым следует e, затем l и т. д.

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

Имеются и другие символы, которые в шаблонах получают особый смысл вместо того, чтобы обозначать самих себя. Такие символы называются метасимволами. Приведём несколько примеров метасимволов, не указывая пока их особый смысл (список не является исчерпывающим): \.-[](){}?*+^$|.

Некоторые символы оказываются метасимволами не всегда, а только тогда, когда попадают в определённый контекст. У некоторых метасимволов в зависимости от контекста оказывается разный смысл.

Если нужно вставить в регулярное выражение метасимвол, лишив его особого смысла, его следует защитить (экранировать), поставив перед ним бэкслэш. Например, знак плюса в регулярное выражение вставляется как \+.

Шаблон [abcdef] обозначает один из символов, перечисленных в квадратных скобках. Если, к примеру, нас интересует слово Hello, неважно, с большой или маленькой буквы, шаблон будет таким: [Hh]ello. Вот шаблон, обозначающий маленькую гласную букву английского алфавита: [aeiouy]. Ещё один пример — символьный класс, состоящий из обеих квадратных скобок: [\[\]].

Если символьный класс включает символы, идущие подряд в кодовой таблице, достаточно указать первый и последний символы, вставив между ними дефис. К примеру, класс, обозначающий любую десятичную цифру, можно задать как [0-9]. Буква английского алфавита обозначается как [A-Za-z] (здесь мы полагаемся на тот факт, что в любой кодовой таблице заглавные и маленькие английские буквы идут непрерывными блоками в алфавитном порядке; однако блок маленьких букв не следует сразу за блоком заглавных).

Можно определить символьный класс, состоящий из всех символов за исключением перечисленных — так называемое отрицание символьного класса. Для этого сразу за открывающей квадратной скобкой перед перечислением вставляется знак циркумфлекса ^. Любой символ, не являющийся цифрой, можно обозначить как [^0-9].

Для некоторых популярных классов символов есть специальные обозначения:

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

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

Наиболее часто используемые анкеры — привязка к началу (^) и концу ($) строки. Привязка к началу строки должна помещаться в начале шаблона, а привязка к концу — в конце.

Например, к шаблону ^анти подходят такие слова русского языка, как антидот, антисемитизм или античастица. Без привязки также подошли бы строки, не начанающиеся с «анти-», но содержащие это буквосочетание внутри, например, меркантилизм. Для поиска слов, заканчивающихся на «-ться» нужен шаблон ться$ (мы уверены почти на 100%, что все такие слова — это возвратные глаголы в инфинитиве). Ничто не мешает применять в шаблоне обе эти привязки.

Другой полезный анкер — привязка к границе между словами \b. Он соответствует месту в строке, находящемуся между символами, один из которых принадлежит классу \w, а другой — \W (в любом порядке). Эта привязка может соответствовать также началу или концу строки (в этом случае считается, что срока как бы окружена воображаемыми символами из класса \W).

Если мы ищем фрагмент, который должен подойти под один из нескольких шаблонов, нужно перечислить эти шаблоны, разделив вертикальной чертой |. Например, понедельник|вторник|среда|четверг|пятница|суббота|воскресенье. Для того, чтобы сделать список альтернатив самостоятельной единицей и отделить от соседних, его нужно заключить в скобки. Например, шаблон Уважаем(ый|ая) означает строку Уважаем, за которой следует одна из строк ый или ая. Без скобок шаблон Уважаемый|ая обозначал бы одну из строк Уважаемый или ая. У скобок имеется важное побочное действие, о котором будет сказано в разделе «Группировка и захват».

Для того, чтобы указать, сколько раз может повторяться шаблон, после него ставят так называемые квантификаторы (от латинского слова quantum — сколько):

квантификаторназначение
*0
+1
?0 или 1
{x}ровно x
{x,}x
{x,y}x и y

Квантификаторы *, + и ? являются избыточными, поскольку они могут быть выражены иначе с помощью фигурных скобок. А именно, * равносилен {0,}, + равносилен {1,}, а ? — то же самое, что и {0,1}. Но данные квантификаторы очень часто используются, и этим заслужили отдельных обозначений.

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

Вот несколько примеров:

Разберём последний пример подробнее. Помимо необязательного минуса в начале, в шаблоне присутствует группа с двумя альтернативами: \d+(\.\d*)? и \.\d+. Первая альтернатива включает обязательную целую часть \d+ (как минимум одна цифра), и следующую за ней необязательную дробную (\.\d*)?. В дробной части, если она есть, имеется десятичная точка, и, возможно, несколько цифр. Таким образом, этой альтернативе соответствуют строки 15, 15., 15.487. Другая альтернатива нужна для строк вида .618 с отсутствующей целой частью — во многих компьютерных языках эта запись имеет право на существование.

[Примечание]Примечание

Было бы ошибкой попытаться объединить эти альтернативы: \d*\.\d*. Тогда одинокая точка как запись числа с нулевой целой и дробной частью была бы законной. Между прочим, во встроенном языке программирования Basic старинного советского микрокомпьютера МК-85 было возможно использовать точку для обозначения числа ноль.

Если простейшие элементы регулярного выражения — символы, символьные классы и анкеры, записываются подряд, это означает, что при поиске в строке по шаблону эти элементы будут сопоставляться с частями строки последовательно, в той же последовательности. Этот порядок нарушается, если применяются альтернативы. Можно представлять себе, что составное регулярное выражение составляется из простейших при помощи двух операций: последовательного соединения (композиции) и альтернативы. Композиция — аналог операции умножения в арифметике. Альтернатива — аналог сложения. Первое сходство с арифметикой состоит в том, что операция альтернативы имеет более низкий приоритет, чем композиция, поэтому могут потребоваться скобки для группировки, как в примере Уважаем(ый|ая).

[Примечание]Примечание

Многие, хотя и не все, законы арифметики действуют и для регулярных выражений:

коммутативность альтернативы
x | y = y | x ;
ассоциативность альтернативы
x | y | z = x | y | z ;
ассоциативность композиции
x y z = x y z ;
дистрибутивность альтернативы относительно композиции (левая и правая)
x y | z = x y | x z , x | y z = x z | y z .

В этой странной арифметике регулярных выражений не имеет место закон коммутативности для композиции. Кроме того, отсутствует аналог нуля из-за очевидного соотношения x | x = x . Роль единицы (правой и левой) для композиции выполняет пустой шаблон (обозначим его 𝟙): 𝟙 x = x 𝟙 = x . Квантификаторы вида {n} играют роль возведения в n-ю степень.

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

Рассмотрим задачу поиска слов, содержащих три одинаковые гласных буквы подряд. Наивное решение [аеёиоуэюя]{3}, использующее квантификаторы, не будет работать, поскольку такому шаблону соответствуют строки с тремя подряд идущими гласными, но необязательно одинаковыми. Чудовищное решение с полным перечислением альтернатив, ааа|еее|ёёё|иии|ооо|ууу|эээ|ююю|яяя, мы с негодованием отвергаем: ведь стоит взять другой, более обширный символьный класс, или заменить тройку в квантификаторе на большее значение, как размер шаблона катастрофически вырастет.

Тем не менее возможно элегантное решение, использующее группы захвата. Захватим гласную в группу, а затем сошлёмся на содержимое буфера захвата. Ссылки на первый, второй, третий буферы записываются в регулярном выражении как \g1, \g2, \g3. Итак, решением будет шаблон ([аеёиоуэюя])\g1{2}. Обратите внимание, что ссылка на буфер захвата должна следовать в регулярном выражении строго после соответствующей группы.

Обратные ссылки могут ссылаться не только на нумерованные буферы, но и на именованные. Такие ссылки имеют вид \k<name>, где, опять же таки, вместо name стоит конкретное имя. Наш пример можно переписать, применяя именованные группы: (?<vowel>[аеёиоуэюя])\k<vowel>{2} (vowel — гласная).

Иногда возникает необходимость в поиске, при котором не делается отличий между строчными и прописными буквами. Такой поиск называется нечуствительным к регистру (case-insensitive). Вместо того, чтобы всюду в шаблоне заменять буквы на двухбуквенные классы (a[Aa], b[Bb], …), Просто заключим шаблон в специальную группу, включающую режим case-insensitive поиска: (?i:⋯). Такая группа не является группой захвата. Если case-insensitive поиск должен быть реализован только в части регулярного выражения, в группу следует поместить только нужную часть.

Наоборот, если какая-то часть регулярного выражения, в которой осуществляется case-insensitive поиск, нуждается в отключении этого режина, то вернуться к обычному, case-sensitive поиску можно, используя группу (?-i:⋯).

Режимы чувствительности/нечуствительности к регистру влияют лишь на буквы. Что считается буквой, а что нет, зависит от языка, как и правила соответсвия между прописными и строчными буквами. С точки зрения английского языка, например, буквой не является символ Щ. В немецком языке имеется буква ß (между прочим, заглавный вариант этой буквы состоит из двух букв SS: Carl Friedrich GaußCARL FRIEDRICH GAUSS).

Информатика-54© А. Н. Швец