Рег выражения. Шпаргалка по регулярным выражениям

Легкое и веселое введение в теорию регулярных выражений от веб-разработчика Джоша Хоукинса — Regex, охватывающее все основные моменты, которые нужно знать новичку.

Вы когда-нибудь работали со строками? Да-да, с теми самыми «массивами символов», которые мы все знаем и любим. Если вы программировали на чем-нибудь, кроме чистого С, с уверенностью можно предположить, что работали, причем не раз. Но что, если вы имеете дело со множеством строк? Или со строками, которые сгенерированы не вашей программой? Например, вы считываете электронное письмо, парсите аргументы командной строки или читаете инструкции, написанные человеком, и вам нужен более структурированный метод работы со всем этим.

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

Введение. Регулярное выражение

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

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

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

Введение. Regex

Сейчас, возможно, мы подошли к месту, где пора испугаться (если вы до сих пор этого не сделали). Мы рассмотрим различия между тем, что вкладывается в понятие регулярных выражений языками программирования, а что - фундаментальной информатикой.

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

Контекстно-зависимые языки ощутимо сложнее и мощнее, так что с этого момента условимся называть регулярные выражения в терминах языков программирования „regex“, дабы подчеркнуть их обособленность от формальных языков в целом.

Учимся писать regex-ы

Регулярные выражения описываются с помощью двух слэшей (// ) и соответствуют строкам, подходящим под шаблон, заключенный между ними. Например, /Hi/ соответствует „Hi“, так что мы можем проверить соответствие некоторой строки этому шаблону.

Символы в регулярных выражениях сопоставляются в том порядке, в котором вводятся. Так /Hello world/ отвечает строке „Hello world“.

Можно упростить поиск произвольных слов, добавив немного regex-магии: \w соответствует любому «слову», составленному только из букв. По такому же принципу идентифицируются числа: \d .

Пример 1

Превосходно, теперь мы можем сравнивать строки или проверять их соответствие некоторому паттерну. Что дальше? Могут ли регулярные выражения выполнять еще какие-нибудь функции?

Будьте уверены! Скажем, мы написали IRC чат бота, который реагирует, если кто-то напишет „Josh“. Наш бот сканирует каждое сообщение, пока не дождется совпадения. Тогда бот отвечает: „Woah, I hope you aren’t talking bad about my pal Josh!“ («О, надеюсь, вы не будете говорить плохо о моем приятеле Джоше!»). Потому что с Джошами дружат только роботы.

Для сравнения строк наш бот использует шаблон /Josh/ . В один прекрасный момент некто по имени Eli обронит: „Eli: Josh, do you really need that much caffeine?“ («Эли: Джош, тебе действительно необходимо такое количество кофеина?»). Наш бот навострит ушки, обнаружит совпадение, выдаст свой неожиданный ответ, чем достаточно напугает Эли. Миссия выполнена! Или нет?

Что, если бы наш бот был более умным? Что, если бы он, например, обращался к говорящему по имени? Что-нибудь вроде „Woah, I hope you aren’t bad-mouthing my buddy Josh, Eli.“ («О, надеюсь, ты не будешь злословить о моем приятеле Джоше, Эли?»).

Квантификаторы (повторяющиеся символы)

0 и более

Мы можем сделать это… Но для начала нужно уяснить пару моментов. Первый - квантификаторы (для повторяющихся символов). Можно использовать * , чтобы обозначить 0 или несколько символов после. Например, /a*/ может соответствовать „aaaaaaa“, а также „“. Да, вы не ослышались: оно будет отвечать пустой строке.

* служит для обозначения чего-то необязательного, так как символ, которому она соответствует, существовать вовсе не обязан. Но он может. И не раз (теоретически, бесчисленное множество раз).
Можно обозначить „Josh“ с помощью /Josh/ , но мы можем также задать „Jjjjjjjjjosh“ или „osh“ паттерном /J*osh/ .

1 и более

Для обозначения одного и более символов используется + . Он эффективно работает по тому же принципу, что и * , за исключением того, что существование хотя бы одного символа более не является опциональным: должен присутствовать по крайней мере один.

Таким образом, мы можем задать шаблоном /J+osh/ строки „Josh“ или „Jjjjjjjjjosh“, но не „osh“.

Метасимволы

Прекрасно, мы уже во многом развязали себе руки. Возможно, сейчас кто-то вопит «Джоооооош», если уже достаточно разозлился…

Но что, если он злится настолько сильно, что даже пару раз ударил лицом по клавиатуре? Как нам обозначить «ааавыопшадлорвпт», не зная заранее, насколько меток его нос?
С помощью метасимволов !

Метасимволы позволяют задавать абсолютно ЧТО УГОДНО. Их синтаксис — . . (Да, точка. Просто точка.). Бьемся об заклад, вы часто пользуетесь ею, так что не стесняйтесь обозначать ей конец предложения.

Можно задать „Joooafhuaisggsh“ выражением /Jo+.*sh/ , комбинируя полученные ранее знания о повторяющихся символах и метасимволах. Если быть точными, данное выражение соответствует одной „J“, одному или более „o“, нулю или нескольким метасимволам, а также одной „s“ и одной „h“. Эти пять блоков подводят нас к тому, что мы называем…

…группами символов

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

Это полезно понять и как отдельную технику, но большую функциональность она обретает в сочетании с повторяющимися символами. Группы символов задаются с помощью круглых скобок (да-да, этих ребят).
Допустим, мы хотим повторять „Jos“, но не „h“. что-то вроде „JosJosJosJosJosh“. Это можно сделать выражением /(Jos)+h/ . Просто, не правда ли?

Но наконец… Возвращаясь к нашему первому примеру, как нам получить имя Эли в нашем IRC чате из отправленного ею сообщения?

Группы символов могут также служить для запоминания подстрок. Для этого обычно делают что-то вроде \1 , чтобы определить первую заданную группу.

Например, /(.+) \1/ — особый случай. Здесь мы видим набор случайных символов, повторяющийся один или более раз, пробел после него, а затем повторение точно такого же набора еще раз. Так что такое выражение будет соответствовать „abc abc“, но не „abc def“, даже если „def“ сам по себе отвечает (.*) .

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

Пример 2

Фух… Наконец-то можно вернуться к примеру с IRC чат ботом. Давайте применим наши знания на практике.

Если мы хотим выцепить имя отправителя сообщения, когда он пишет „Josh“, наше выражение будет выглядеть примерно так: /(\w+): .*Josh.*/ , и мы сможем сохранить результат в переменной в нашем языке программирования для ответа.

Давайте рассмотрим наше регулярное выражение. Здесь одна или более букв, следующих за «: », 0 или более символов, „Josh“ и снова 0 или более символов.

Заметка: /.*word.*/ — простой способ задать строку, содержащую „word“, причем другие символы в ней могут присутствовать, а могут и нет.

На Python это будет выглядеть следующим образом:
import re
pattern = re.compile(ur"(\w+): .*Josh.*") # Our regex
string = u"Eli: Josh go move your laundry" # Our string
matches = re.match(pattern, string) # Test the string
who = matches.group(1) # Get who said the message
print(who) # "Eli"
Заметьте, что мы использовали .group(1) точно так же, как \1 . В этом нет ничего нового, за исключением использования регулярных выражений в Питоне.

Начало и конец

До этого момента мы предполагали, что искомые подстроки могут находиться в любом месте строки. К примеру, /(Jos)+h/ соответствует любой строке, которая содержит „Jos-повторяющееся-h“ в произвольном месте.

А что, если нам необходимо, чтобы строка начиналась с этого шаблона? Это можно обозначить как /^(Jos)+h/ , где ^ соответствует началу строки. Аналогично, $ обозначает конец строки.

Теперь, если мы хотим задать строку, содержащую только „Jos-повторяющееся-h“, то напишем /^(Jos)+h$/ .

Перечисление выражений

Представьте, что вы пишете регулярное выражение для рецепта бутерброда. Вы не знаете, предпочитает заказчик белый хлеб или черный, но выбрать все равно придется только один. Как добавить возможность выбора в regex? С помощью перечислений !

Они позволяют задавать наборы возможных значений для группы символов. Это выглядит следующим образом: (white|wheat) . В контексте нашего примера с бутербродом, будет принят один из вариантов - либо „white“, либо „wheat“.

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

Модификаторы

Мы говорили о regex с /двумя слэшами/, верно? Мы знаем, что находится между ними, но что должно быть снаружи?

Неожиданный поворот: ничего!

…слева. Правая сторона, напротив, может содержать множество, множество всего полезного. Даже стыдно, что мы так долго не сказали об этом ни слова!
Модификаторы задают правила, по которым применяются регулярные выражения.

Вот список основных модификаторов (с Regex101.com):

Модификатор Название Описание
g global Все совпадения
m multi-line ^ и $ соответствуют началу и концу каждой строки
i insensitive Регистронезависимое сравнение
x extended Пробелы и текст после # игнорируются
X extra \ с произвольной буквой, не имеющей особого значения, возвращает ошибку
s single line Игнорирует символы новой строки
u unicode Строки-шаблоны обрабатываются как UTF-16
U ungreedy По умолчанию в regex используется «ленивая квантификация». Модификатор U делает квантификацию «жадной»
A anchored Шаблон форсируется к ^
J duplicate Разрешает дублирующиеся имена субпаттерннов

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

Предположим, Эли взбесилась настолько, что начала бомбить чат сообщениями с БуквАМи рАЗных РегИсТРОв. Это нас не страшит, потому что i уже здесь! Мы можем легко задать гневливое выражение „I hAate LiVing witH JOSH!!!“ паттерном /i ha+te living with josh!+/i . Теперь наши regex стали легче читаемы, а также намного более мощны и полезны. Замечательно!

Вы можете самостоятельно поиграть с разными модификаторами. На мой взгляд, в целом вам больше всего пригодится igm .

Что дальше?

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

Существует множество символов и их сочетаний, используемых в регулярных выражениях. Обычно вы будете натыкаться на них во время изучения Stack Overflow, но о значении некоторых можно догадаться и из предыдущих примеров (например, \n - символ перехода на новую строку). База заложена, но выучить предстоит еще очень многое.

Найти полный список сочетаний символов, а также проверить свои знания можно .
Если это показалось для вас проще простого, попробуйте regex-кроссворды . Они действительно заставят вас попотеть.

После точки

Эта статья — перевод гайда Джоша Хоукинса. Джош — страстный веб-разработчик из Алабамы. Он начал программировать в возрасте девяти лет, сосредоточившись на видеоиграх, десктопных и некоторых мобильных приложениях. Однако, во время стажировки в 2015, Джош открыл для себя веб-разработку и ворвался в мир опенсорса, связанного с этой областью.

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

На первый взгляд регулярные выражения выглядят страшновато (ну хорошо, на второй - еще страшнее;)).

Я настоятельно рекомендую Вам “поиграть” с поставляемой в дистрибутиве демо-программой для Windows REStudio - это позволит Вам лучше понять принцип работы регулярных выражений и отладить Ваши собственные выражения. Кроме того, в TestRExp входит множество примеров выражений.

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

Давайте начнем наше знакомство с регулярными выражениями!

Простое сравнение

Любой символ совпадает с самим собой, если он не относится к специальным метасимволам описанным чуть ниже.

Последовательность символов совпадает с такой же последовательностью во входной строке, так что шаблон bluh совпадет с подстрокой bluh во входной строке. Пока все просто, не так ли?

Если необходимо, чтобы метасимволы или escape-последовательности воспринимались как обычные символы, их нужно предварять символом \ , например, метасимвол ^ обычно совпадает с началом строк, однако, если записать его как \^ , то он будет совпадать с символом ^ , \\ совпадает с \ и т.д.

Примеры:

foobar находит ‘foobar’ \^FooBarPtr находит ‘^FooBarPtr’

Escape-последовательности

Любой символ может быть определен с помощью escape последовательности, так же как это делается в языках C или Perl: \n означает начало строки, \t - табуляцию и т.д.. Вообще, \xnn , где nn это последовательность шестнадцатеричных цифр, означает символ с ASCII-кодом nn. Если необходимо определить двухбайтный (Unicode) символ, используйте формат \x{nnnn} , где nnnn - одна или более шестнадцатеричных цифр.

\xnn символ с шестнадцатеричным кодом nn \x{nnnn} символ с шестнадцатеричным кодом nnnn (более одного байта можно задавать только в режиме (tregexpr_interface.html#unicode))| \t табуляция (HT/TAB), можно также \x09 \n новая строка (NL), можно также \x0a \r возврат каретки (CR), можно также \x0d| \f перевод формата (FF), можно также \x0c| \a звонок (BEL), можно также \x07| \e escape (ESC), можно также \x1b|

Примеры:

foo\x20bar находит ‘foo bar’ (обратите внимание на пробел посередине) \tfoobar находит ‘foobar’ предшествуемый табуляцией

Перечни символов

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

Если первый символ перечня (сразу после [) - ^ , то такой перечень совпадает с любым символом не перечисленным в перечне.

Примеры:

foobr находит ‘foobar’, ‘foober’ и т.д. но не ‘foobbr’, ‘foobcr’ и т.д. foob[^aeiou]r находит ‘foobbr’, ‘foobcr’ и т.д.. но не ‘foobar’,’foober’ и т.д.

Внутри перечня символ - может быть использован для определения диапазонов символов, например a-z представляет все символы между a и z , включительно.

Если Вам необходимо включить в перечень сам символ - , поместите его в начало или конец перечня или предварите \ . Если Вам необходимо поместить в перечень сам символ ] , поместите его в самое начало или предварите \ .

Примеры:

[-az] "a", "z" и "-" "a", "z" и "-" "a", "z" и "-" все 26 малых латинских букв от "a" до "z" [\n-\x0D] #10, #11, #12, #13. [\d-t] цифра, "-" или "t". -a] символ из диапазона "]".."a".

Метасимволы

Метасимволы - это специальные символы, являющиеся важнейшим понятием в регулярных выражениях. Существует несколько групп метасимволов.

Метасимволы - разделители строк

^ начало строки $ конец строки \A начало текста \Z конец текста. любой символ в строке

Примеры:

^foobar находит "foobar" только если он в начале строки foobar$ находит "foobar" только если он в конце строки ^foobar$ находит "foobar" только если это единственное слово в строке foob.r находит "foobar", "foobbr", "foob1r" и т.д.

Метасимвол ^ по умолчанию совпадает только в начале входного текста, а метасимвол $ - только в конце текста. Внутренние разделители строк, имеющиеся в тексте, не будут совпадать с ^ и $ .

Однако, если Вам необходимо работать с текстом как с многострочным, чтобы ^ совпадал после каждого разделителя строки внутри текста, а $ - перед каждым разделителем, то Вы можете включить модификатор /m .

Метасимволы \A и \Z аналогичны ^ и $ , но на них не действует модификатор /m , т.е. они всегда совпадают только с началом и концом всего входного текста.

Метасимвол. по умолчанию совпадает с любым символом, однако, если Вы выключите модификатор /s , то. не будет совпадать с разделителями строк.

TRegExpr интерпретирует разделители строк так, как это рекомендовано на www.unicode.org :

^ совпадает с началом входного текста, а также, если включен модификатор /m , с точкой непосредственно следующей после \x0D\x0A , \x0A или \x0D (если Вы используете Unicode-версию

$ совпадает с концом входного текста, а также, если включен модификатор /m , с точкой непосредственно предшествующей \x0D\x0A , \x0A или \x0D (если Вы используете Unicode-версию TRegExpr, то также \x2028 или \x2029 или \x0B или \x0C или \x85). Обратите внимание, что он не совпадает в промежутке внутри последовательности \x0D\x0A .

Совпадает с любым символом, но если выключен модификаторr /s , то. не совпадает с \x0D\x0A и \x0A и \x0D (если Вы используете Unicode-версию TRegExpr, то не совпадает также с \x2028 и \x2029 и \x0B и \x0C и \x85).

Обратите внимание, что ^.*$ (шаблон для пустой строки) не совпадает с пустой строкой вида \x0D\x0A , но совпадает с \x0A\x0D .

Вы можете перенастроить вышеописанное поведение при обработке многострочных текстов - см. описания свойств LineSeparators и LinePairedSeparator , скажем, Вы можете перенастроиться на использование только Unix-разделителей строк \n или только DOS/Windows-разделителей \r\n или же смешанных разделителей (так и настроено по умолчанию) или вообще определить свои собственные разделители строк!

Метасимволы - стандартные перечни символов

\w буквенно-цифровой символ или "_" \W не \w \d цифровой символ \D не \d \s любой "пробельный" символ (по умолчанию - [ \t\n\r\f]) \S не \s

Стандартные перечни \w , \d и \s можно использовать и внутри перечней символов.

Примеры:

foob\dr находит "foob1r", ""foob6r" и т.д. но не "foobar", "foobbr" и т.д. foob[\w\s]r находит "foobar", "foob r", "foobbr" и т.д. но не "foob1r", "foob=r" и т.д.

Метасимволы - варианты

Вы можете определить перечень вариантов, используя метасимвол | для их разделения, например fee|fie|foe найдет fee или fie или foe , (так же как f(e|i|o)e). В качестве первого варианта воспринимается все от предыдущего метасимвола (или [ или от начала выражения до первого метасимвола | , в качестве последнего - все от последнего | до конца выражения или до ближайшего метасимвола) . Обычно, чтобы не запутаться, набор вариантов всегда заключают в скобки, даже если без этого можно было бы обойтись.

Варианты пробуются начиная с первого и попытки завершаются сразу же как удастся подобрать такой при котором совпадет вся последующая часть выражения (подробнее см.Механизм работы). Это означает, что варианты не обязательно обеспечат “жадное” поведение. Например, если применить выражение foo|foot ко входной строке barefoot , то будет найдено foo так это первый вариант который позволил совпасть всему выражению.

Обратите внимание, что метасимвол | воспринимается как обычный символ внутри перечней символов, например, означает ровно то же самое что и .

Примеры:

foo(bar|foo) находит "foobar" или "foofoo".

Метасимволы - подвыражения

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

  • по завершении поиска выражения Вы можете обратиться к любому подвыражению используя свойства MatchPos, MatchLen и Match , а также подставлять подвыражения в некий шаблон, используя метод Substitute).

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

Первое подвыражение имеет номер 1 (выражение в целом - 0" , к нему можно обращаться в Substitute как $0" так и $&).

Примеры:

(foobar){8,10} находит строку содержащую 8, 9 или 10 копий "foobar" foob(|a+)r находит "foob0r", "foob1r" , "foobar", "foobaar", "foobaar" и т.д.

Метасимволы - обратные ссылки

Метасимволы от \1 до \9 воспринимаются как обратные ссылки. \ совпадает с ранее найденным подвыражением # .

Примеры:

(.)\1+ находит "aaaa" и "cc". (.+)\1+ также находит "abab" и "123123" ([""]?)(\d+)\1 находит "13" (в дв.кавычках), или "4" (в один.кавычках) или 77 (без кавычек) и т.д.

Модификаторы

Модификаторы служат для изменения режимов работы TRegExpr.

Вы можете изменять модификаторы несколькими способами.

Любой модификатор может меняться с помощью специальной конструкции (?…) внутри регулярного выражения.

Также, Вы можете присвоить значение соответствующему свойству экземпляра объекта TRegExpr (например, ModifierX для изменения модификатора /x, или ModifierStr для изменения сразу нескольких модификаторов). Значения по умолчанию для новых экземпляров объектов TRegExpr определены в , например RegExprModifierX определяет значение по умолчанию для ModifierX.

i

Регистро-независимый режим (по умолчанию использует выбранный в ОС язык по умолчанию), (см. также InvertCase)

m

s

g

Не стандартный модификатор. Выключая его Вы переключаете все повторители в “не жадный” режим (по умолчанию этот модификатор включен). Т.е. если его отключить, то все + работают как +? , * как *? и т.д.

x

Позволяет форматировать шаблон чтобы обеспечить более легкую читаемость (см. описание ниже).

r

Не стандартный модификатор. Если включен, то диапазоны вида а-я включают также букву ё , А-Я включают Ё , а а-Я включает вообще все русские буквы.

Модификатор /x заставляет TRegExpr игнорировать пробелы, табуляции и разделители строк, что позволяет форматировать текст выражения. Кроме того, если встречается символ #, то все последующие символы до конца строки воспринимаются как комментарий, например:

((abc) # Комментарий 1 | # Пробелы внутри выражения также игнорируются (efg) # Комментарий 2)

Естественно, это означает что, если Вам нужно вставить в выражение пробел, табуляцию или разделитель строки или # , то в расширенном (/x) режиме это можно сделать только предваряя их / или используя /xnn (внутри перечней символов все эти символы воспринимаются как обычно)

Расширения Perl

(?imsxr-imsxr)

Позволяет изменять значения модификаторов

Примеры:

(?i)Saint-Petersburg находит "Saint-petersburg" и "Saint-Petersburg" (?i)Saint-(?-i)Petersburg находит "Saint-Petersburg" но не "Saint-petersburg" (?i)(Saint-)?Petersburg находит "Saint-petersburg" и "saint-petersburg" ((?i)Saint-)?Petersburg находит "saint-Petersburg", но не "saint-petersburg"

Давно хотели изучить regexp? Это небольшое руководство поможет разобраться с ними в 6 этапов, а обилие примеров позволит закрепить материал.

Что такое regexp?

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

Регулярное выражение – это шаблон, который сравнивается с предметной строкой слева направо. Словосочетание “regular expression” применяется не так широко, вместо него обычно употребляют “regex” и “regexp”. Регулярное выражение используется для замены текста внутри строки, проверки формы, извлечения подстроки из строки на основе соответствия шаблона и т. д.

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

Это выражение принимает строки john_doe , jo-hn_doe и john12_as . Однако имя пользователя Jo не будет соответствовать этому выражению, потому что оно содержит прописную букву, а также является слишком коротким.

1. Базовые совпадения

Регулярное выражение — это всего лишь шаблон из символов, который мы используем для выполнения поиска в тексте. Например, регулярное выражение the означает букву t , за которой следует буква h , за которой следует буква e .

"the" => The fat cat sat on the mat.

Регулярное выражение 123 соответствует строке 123 . Регулярное выражение сопоставляется входной строке путем сравнения каждого символа в regexp с каждым символом входной строки. Регулярное выражение и входная строка сравниваются посимвольно. Обычно regex чувствительны к регистру, поэтому The не соответствует строке the .

"The" => The fat cat sat on the mat.

2. Метасимволы

Метасимволы служат строительными блоками regexp. Они не являются независимыми и обычно интерпретируются каким-либо образом. Некоторые метасимволы имеют особое значение, а потому помещаются в квадратные скобки. Метасимволы:

Метасимволы Описание
. Любой единичный символ, исключая новую строку.
Поиск набора символов, помещенных в скобки.
[^ ] Negated character class. Matches any character that is not contained between the square brackets
* 0 или больше повторений предшествующего символа.
+ 1 или больше повторений предшествующего символа.
? Делает предшествующий символ опциональным.
{n,m} Возвращает как минимум «n», но не более «m» повторений предшествующего символа.
(xyz) Находит группу символа в строго заданном порядке.
| Разделяет допустимые варианты.
\ Исключает следующий символ. Позволяет искать служебные символы () { } . * + ? ^ $ \ |
^ Находит начало введенной строки.
$ Находит конец введенной строки.

2.1 Точка

. — это простейший пример метасимвола. Метасимвол. соответствует любому единичному символу. Например, регулярное выражение.ar означает: любой символ, за которым следует буква a , за которой следует буква r .

«.ar» => The car par ked in the gar age.

2.2 Интервал символов

Интервал или набор символов также называют символьным классом. Для его обозначения используются квадратные скобки. Чтобы указать диапазон символов внутри класса, необходимо поставить знак тире. Порядок ряда символов в наборе неважен. Так, например, регулярное выражение he означает: T или t , за которым следует буква h , за которой следует буква e .

«he » => The car parked in the garage.

Стоит отметить, что точка, помещенная в квадратные скобки, означает именно точку, а ничто другое. Таким образом регулярное выражение ar[.] означает строчный символ a , за которым следует буква r , за которой следует точка. .

«ar [.]» => A garage is a good place to park a car.

2.2.1 Отрицание набора символов

Обычно символ ^ представляет начало строки, но когда он внутри квадратных скобок, все символы, которые находятся после него, исключаются из шаблона. Например, выражение [^c]ar поможет отыскать все символы кроме c , за которыми следуют а и r .

"[^c]ar" => The car par ked in the gar age.

2.3 Повторения

Следующие мета-символы + , * или? используются для того, чтобы обозначить допустимое количество повторения подшаблона. Их роль зависит от конкретного случая.

2.3.1 Звездочка

Этот символ поможет найти одно или более копий какого-либо символа. Регулярное выражение a* означает 0 или более повторений символа a. Но если этот символ появится после набора или класса символов, тогда будут найдены повторения всего сета. Например, выражение * означает любое количество этих символов в строке.

"*" => The car parked in the garage #21.

Также символ может быть использован вместе с метасимволом. для подбора строки из любых символов.* .

Еще звездочку можно использовать со знаком пробела \s , чтобы подобрать строку из пробелов. Например, выражение \s*cat\s будет означать 0 или более пробелов, за которыми следует символ с, за ним а и t , а за ними снова 0 либо больше пробелов.

"\s*cat\s*" => The fat cat sat on the concat enation.

2.3.2 Плюс

Соответствует одному или нескольким повторениям предыдущего символа. Например, регулярное выражение c.+t означает: строчная буква c , за которой следует хотя бы один символ, за которым следует строчный символ t . Необходимо уточнить, что буква t должна быть последней t в предложении.

"c.+t" => The fat cat sat on the mat .

2.3.3. Вопросительный знак

В regexp метасимвол? делает предшествующий символ необязательным. Этот символ соответствует полному отсутствию или же одному экземпляру предыдущего символа. Например, регулярное выражение [T]?he означает: необязательно заглавную букву T , за которой следует строчный символ h , за которым следует строчный символ e .
"[T]he" => The car is parked in the garage.
Тестировать выражение

"[T]?he" => The car is parked in the garage.

2.4 Скобки

Скобки в regexp, которые также называются квантификаторами, используются для указания допустимого количества повторов символа или группы символов. Например, регулярное выражение {2,3} означает, что допустимое количество цифр должно быть не менее двух цифр, но не более 3 (символы в диапазоне от 0 до 9).

"{2,3}" => The number was 9.999 7 but we rounded it off to 10 .0.

Мы можем убрать второе число. Например, выражение {2,} означает 2 или более цифр. Если мы также уберем запятую, то тогда выражение {3} будет находить только лишь 3 цифры, ни меньше и ни больше.

"{2,}" => The number was 9.9997 but we rounded it off to 10 .0.

"{3}" => The number was 9.999 7 but rounded it off to 10.0.

2.5 Символьная группа

Группа символов — это группа подшаблонов, которая записывается внутри скобок (...) . Как было упомянуто раньше, если в регулярном выражении поместить квантификатор после символа, он повторит предыдущий символ. Но если мы поставим квантификатор после группы символов, он просто повторит всю группу. Например, регулярное выражение (ab)* соответствует нулю или более повторениям символа «ab». Мы также можем использовать | — метасимвол чередования внутри группы символов. Например, регулярное выражение (c|g|p)ar означает: символ нижнего регистра c , g или p , за которым следует символ a , за которым следует символ r .

"(c|g|p)ar" => The car is par ked in the gar age.

2.6 Перечисление

В regexp вертикальная полоса | используется для определения перечисления. Перечисление — это что-то вроде условия между несколькими выражениями. Можно подумать, что набор символов и перечисление работают одинаково, но это совсем не так, между ними существует огромная разница. Перечисление работает на уровне выражений, а набор символов на уровне знаков. Например, регулярное выражение (T|t)he|car означает: T или t , сопровождаемая строчным символом h , сопровождаемый строчным символом e или строчным символом c , а затем a и r .

"(T|t)he|car" => The car is parked in the garage.

2.7 Исключение специального символа

Обратная косая черта \ используется в regexp, чтобы избежать символа, который следует за ней. Это позволяет нам указывать символ в качестве символа соответствия, включая зарезервированные { } / \ + * . $ ^ | ? . Чтобы использовать специальный символ в качестве подходящего, перед ним нужно поставить \ .

Например, регулярное выражение. используется для нахождения любого единичного символа. Регулярное выражение (f|c|m)at\.? означает строчную букву f , c или m , а затем a , за ней t с последующим дополнительным символом. .

"(f|c|m)at\.?" => The fat cat sat on the mat .

2.8 Анкеры — Привязки

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

2.8.1. Caret

Символ ^ используется в regexp, чтобы проверить, является ли соответствующий символ первым символом в введенной строке. Если мы применяем следующее регулярное выражение ^a (проверяем является ли a первым символом) для введенной строки abc , то оно будет равно a . Но если мы применим регулярное выражение ^b к той же строке, то оно ничего не вернет, потому что во входной строке abc символ «b» не является первым. Давайте посмотрим на другое регулярное выражение ^(T|t)he , которое означает: T или t — это символ начала входной строки, за которым следует строчный символ h , а затем e .

"(T|t)he" => The car is parked in the garage.

"^(T|t)he" => The car is parked in the garage.

2.8.2 Доллар

Знак доллара используется для проверки, является ли символ в выражении последним в введенной строке. Например (at\.)$ означает строчную а, за которой следует t , за которой следует a . , которые должны заканчивать строку.

"(at\.)" => The fat cat. sat. on the mat.

"(at\.)$" => The fat cat. sat. on the mat.
Тестировать выражение

3. Сокращения для обозначения символов

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

4. Lookaround Позиционная проверка

Lookbehind и lookahead (также называемые lookaround) — это определенные типы non-capturing групп (Они используются для поиска, но сами в него не входят). Lookaheads используются, когда у нас есть условие, что этому шаблону предшествует или следует другой шаблон. Например, мы хотим получить все числа, которым предшествует символ $ из входной строки $4.44 and $10.88 . Мы будем использовать регулярное выражение (?<=\$)* , которое означает: получить все числа, содержащие. и которым предшествует символ $ . Ниже приведены lookarounds, что используются в регулярных выражениях:

4.1 Положительный Lookahead

Положительный lookahead означает, что эта часть выражения должна следовать за впереди идущим выражением. Возвращаемое значение содержит текст, который совпадает с первой частью выражения. Чтобы определить позитивный lookahead, используют скобки. Внутри них размещают знак вопроса и знак равенства: (?=...) . Само же выражение пишется после = . Например, выражение (T|t)he(?=\sfat) — это T в верхнем или нижнем регистре, за которым следует h и e . В скобках мы определяем позитивный lookahead, который говорит движку регулярного выражения искать The или the , за которыми следует fat .

"(T|t)he(?=\sfat)" => The fat cat sat on the mat.

4.2 Отрицательный Lookahead

Негативный lookahead используется, когда нам нужно получить все совпадения в строке, за которой не следует определенный шаблон. Негативный lookahead определяется так же, как и позитивный, с той лишь разницей, что вместо знака равенства мы используем знак отрицания! . Таким образом, наше выражение приобретает следующий вид: (?!...) . Теперь рассмотрим (T|t)he(?!\sfat) , что означает: получить все The или the в введенной строке, за которыми не следует слово fat , предшествующее знаку пробела.

"(T|t)he(?!\sfat)" => The fat cat sat on the mat.

4.3 Положительный Lookbehind

Положительный lookbehind используется для получения всех совпадений, которым предшествует определенный шаблон. Положительный lookbehind обозначается так: (?<=...) . Например, регулярное выражение (?<=(T|t)he\s)(fat|mat) означает получить все fat или mat из строки ввода, которые идут после слова The или the .

"(? The fat cat sat on the mat .

4.4 Отрицательный Lookbehind

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

5. Флаги

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

5.1 Нечувствительные к регистру

Модификатор i используется для поиска совпадений, нечувствительных к регистру. Например, выражение /The/gi означает прописную букву T , за которой следуют h и e . И в самом конце выражения стоит i , благодаря которому можно проигнорировать регистр. g применяется для того, чтобы найти шаблон во всей введенной строке.
"The" => The fat cat sat on the mat.
Тестировать выражение

"/The/gi" => The fat cat sat on the mat.

5.2 Глобальный поиск

Модификатор используется для выполнения глобального поиска шаблона(поиск будет продолжен после первого совпадения). Например, регулярное выражение /.(at)/g означает любой символ, кроме новой строки, за которым следует строчный символ a , а затем t . Поскольку мы использовали флаг g в конце регулярного выражения, теперь он найдет все совпадения в вводимой строке, а не только в первой (что является стандартом).

"/.(at)/" => The fat cat sat on the mat.

"/.(at)/g" => The fat cat sat on the mat .

5.3 Многострочный поиск

Модификатор m нужен для выполнения многострочного поиска. Как было сказано раннее, привязки (^, $) используются для проверки, является ли шаблон началом или концом строки. Но если мы хотим, чтобы привязки работали в каждой строке, нужно использовать флаг m . Например, регулярное выражение /at(.)?$/gm означает: строчный символ a , за которым следует t и что угодно, только не новая строка. А благодаря флагу m этот механизм регулярных выражений соответствует шаблону в конце каждой строки строки.

"/.at(.)?$/" => The fat
cat sat
on the mat .

"/.at(.)?$/gm" => The fat
cat sat

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

Все изменилось, когда мне пришлось более плотно работать с Google Analytics и Google Tag Manager в Netpeak.

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

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

Что такое регулярные выражения

Регулярные выражения (regular expressions, RegExp) — наборы символов, применяемых для поиска текстовых строк, соответствующих требуемым условиям. Результат применения регулярного выражения — подмножество данных, отобранное согласно логике, заложенной в выражении. Регулярные выражения применяются в любых задачах по поиску в множестве данных, для которых нужно получать выжимку по определенным правилам.

Синтаксис регулярных выражений

Большинство символов в регулярных выражениях представляют сами себя, за исключением группы специальных символов « \ / ^ $ . | ? * + () { }». Если эти символы нужно представить в качестве символов текста, их следует экранировать обратной косой чертой «\».

Если эти спецсимволы встречаются без обратной косой черты, значит у них особенные значения в регулярных выражениях:

  • «^» — каретка, циркумфлекс или просто галочка. Начало строки;
  • «$» — знак доллара. Конец строки;
  • «.» — точка. Любой символ;
  • «*» - знак умножения, звездочка. Любое количество предыдущих символов;
  • «+» - плюс. 1 или более предыдущих символов;
  • «?» - вопросительный знак. 0 или 1 предыдущих символов;
  • «()» - круглые скобки. Группировка конструкций;
  • «|» - вертикальная линия. Оператор «ИЛИ»;
  • «» - квадратные скобки. Любой из перечисленных символов, диапазон. Если первый символ в этой конструкции - «^», то массив работает наоборот - проверяемый символ не должен совпадать с тем, что перечислено в скобках;
  • «{ }» - фигурные скобки. Повторение символа несколько раз;
  • «\» - обратный слеш. Экранирование служебных символов.

Также существуют специальные метасимволы, ими можно заменить некоторые готовые конструкции:

  • \b — обозначает не символ, а границу между символами;
  • \d — цифровой символ;
  • \D — нецифровой символ;
  • \s — пробельный символ;
  • \S — непробельный символ;
  • \w — буквенный или цифровой символ или знак подчеркивания;
  • \W — любой символ, кроме буквенного или цифрового символа или знака подчеркивания.

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

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

1. Изучаем регулярные выражения в текстовом редакторе

  • в большинстве случаев спецсимволы не нужно экранировать;
  • Notepad++ сохраняет конструкции предыдущих запросов;

2. Проверяем знания регулярных выражений в Regex

Чтобы просканировать все URL адреса только первого уровня вложенности, в сервисе нужно задать такие настройки:

Секреты регулярных выражений (regular expressions)

Часть 1. Диалекты и возможности. Составление регулярных выражений

Серия контента:

1. Введение. Используем ли мы регулярные выражения в полной мере?

Если задуматься над вопросом: "А что такое "регулярное выражение" вообще?", то ответ найдётся не сразу. Можно сказать, что это специализированный язык описания символьного шаблона (последовательности символов) поиска в строках текста. Здесь важно то, что при поиске совпадений выполняется именно посимвольное сравнение. Автор энциклопедии по регулярным выражениям (Mastering Regular Expressions) Джеффри Фридл (J.E.F. Friedl) советует развивать привычку буквально интерпретировать регулярные выражения. Например, глядя на шаблон "^cat", обозначающий "строка должна начинаться со слова cat", следует рассуждать так: "совпадение будет найдено, если мы находимся в начале строки и обнаруживаем символ c, непосредственно за которым располагается символ a, сразу после которого находится символ t". Это позволяет максимально точно оценить смысл и сущность регулярного выражения.

Большинство пользователей знают, что для поиска достаточно задать слово-образец. Например, в Web-браузере в поле "Поиск" после ввода "Linux" вы получите длинный список ссылок на страницы, в тексте которых найдено совпадение с заданным шаблоном "Linux". В локальной файловой системе используется команда grep "Linux" или графические средства поиска.

Не все, но многие пользователи умеют применять метасимволы (* . ?) в шаблонах поиска. Ещё меньшее количество людей знает о возможности применения модификаторов и других изощрённых средств для конструирования регулярных выражений, т.е. во многих случаях мощность механизма регулярных выражений используется едва ли на треть. Отчего бы не попытаться увеличить к.п.д.?

2. Различные диалекты регулярных выражений. Соответствие стандарту POSIX

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

Программы vi(m), sed, grep, less, ed, expr, lex понимают только простые регулярные выражения, а утилиты (g)awk, egrep, а также интерпретаторы языков Perl, Tcl, Python – расширенные регулярные выражения. В то же время в каждой из программ существуют собственные усовершенствования, т.е. создаются поддиалекты регулярных выражений. Рассмотрим сходства и различия этих диалектов.

2.1. Общая схема регулярного выражения

Как правило, регулярное выражение состоит из трёх основных частей:

  1. Якорь – определяет позицию шаблона в строке текста:
    • ^ – якорь, определяющий начало строки;
    • $ – якорь, определяющий конец строки.
  2. Набор (последовательность) символов – для поиска соответствий в заданных позициях строки текста:
    • символ "точка" (.) соответствует любому произвольному символу;
    • алфавитно-цифровые символы и пробел представляют сами себя;
    • прочие символы – интерпретация зависит от диалекта.
  3. Модификатор – задаёт количество повторов предыдущего символа или набора символов (в зависимости от диалекта):
    • * – любое количество повторов символа/набора, в том числе и нулевое;
    • ? – соответствует нулю или одному экземпляру символа/набора;
    • + – соответствует одному или большему количеству экземпляров символа/набора.

Пример: необходимо найти все директивы определения макроконстант в исходном коде на языке С.

grep "^ *#define.*" *.c *.h

Здесь учтено, что в начале строки макроопределения может быть вставлено любое количество пробелов или же пробелы отсутствуют. Часть шаблона #define является литеральной, т.е. каждый символ интерпретируется "как есть". Заключительная часть шаблона означает "любые символы в любых количествах".

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

2.2. Определение диапазонов символов в регулярных выражениях

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

  • – соответствует одному цифровому символу из заданного набора;
  • [аеёиоуыэюя] – соответствует одной из перечисленных гласных букв;
  • [,.:;] – соответствует одному из символов пунктуации.

Обратите внимание на то, что в последнем случае точка в квадратных скобках утрачивает свой особый статус и обозначает не "любой символ", а собственно символ "точка".

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

Имеется также возможность исключать заданные наборы символов из поиска, которая осуществляется следующим образом:

  • [^0-9] – соответствует любому символу, кроме цифрового;
  • [^аеёиоуыэюя] – соответствует любой НЕ гласной букве.

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

2.3. Модификаторы количества повторений символов

Здесь сложность состоит в том, что модификатор * для поиска IP-адреса не годится – попытка использовать шаблон *\.*\.*\. приведёт к выводу строк, содержащих элементы типа 2344.5657.11.00000, не являющихся IP-адресами. Для уточнения количества повторений наборов символов применяется модификатор \{min,max\}. Зная, что в каждой части IP-адреса может содержаться от одной до трёх цифр, запишем модификатор в виде \{1,3\}. Символы "обратный слэш" перед точками необходимы для того, чтобы отменить их специальный статус универсального метасимвола. Также следует учесть, что значение 0 не используется в качестве первого байта обычных IP-адресов. В итоге получим следующий шаблон поиска:

grep "\{0,2\}\.\{1,3\}\.\{1,3\}\.\{1,3\}" *.txt

Модификатор \{min,max\} работает только в простых регулярных выражениях. В расширенных регулярных выражениях нельзя использовать конструкции \{ \}, но можно применять модификатор? в качестве эквивалента выражения \{0,1\}, а модификатор + как эквивалент выражения \{1,\}. Во втором случае после запятой не указано числовое значение – это означает, что максимальное количество совпадений не ограничено.

2.4. Запоминание и повторное использование элемента шаблона

Этот механизм также работает только в простых регулярных выражениях. (Впрочем, в языках программирования Perl, Python и т.п. данный механизм поддерживается – граница между диалектами становится всё менее различимой, помните?)

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

  • \(\)\(\)\2\1 – для пятибуквенных палиндромов (например, level, rotor, madam и т.д.)
  • \(\)\(\)\(\)\3\2\1 – для шестибуквенных палиндромов (например, redder, succus, terret и т.д.)

2.5. Соответствие стандарту POSIX

Стандарт POSIX также делит регулярные выражения на две категории: BRE (Basic Regular Expressions) и ERE (Extended Regular Expressions). В обеих категориях поддерживаются метасимволы. и *, якоря ^ и $, группирование символов в скобках (для BRE скобки экранируются обратным слэшем), применение квантификаторов \{min,max\} к группам в скобках. Запоминание и повторное использование \1...\9 поддерживает только категория BRE, а квантификаторы + и? и конструкцию выбора – только категория ERE.

В стандарте POSIX используется понятие локального контекста (locale) – совокупности параметров, описывающих языковые и культурные правила: формат даты и времени, интерпретация символов активной кодировки и т.д. Это не относится напрямую к регулярным выражениям, но влияет на их функционирование. При работе в локальном контексте с кодировкой UTF-8, принятой почти во всех современных дистрибутивах, корректно обрабатываются символы русского алфавита и их диапазоны, т.е. можно указывать диапазоны [а-я] и [А-Я] в шаблонах поиска.

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

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

3.1. Пример шаблона для поиска денежной суммы, записываемой в формате "10000 руб. 00 коп."

\{1,\} руб\. \{2\} коп\.

Необходимое пояснение: если в модификаторе типа \{min,max\} отсутствует и запятая, и максимальное значение, то такая конструкция задаёт точное количество ожидаемых повторов элемента шаблона. В нашем примере определяются ровно два цифровых символа для обозначения копеек.

3.2. Пример шаблона для поиска URL-строки, соответствующей Web-ресурсу в Интернете:

http://\{1,\}\.[-a-zA-Z0-9_]\{1,\}/*

Необходимое пояснение: дефис теряет своё специальное значение, если он указан в самой первой позиции сразу после открывающей квадратной скобки в диапазоне. По данному шаблону могут быть найдены и такие "экзотические" URL-строки, как, например, http://my.home-server/

В формате расширенных регулярных выражений этот шаблон можно было бы записать более компактно:

http://+\.[-a-zA-Z0-9_]+/*

Такую запись понимают, например, утилиты egrep и awk.

3.3. Шаблон для поиска любого HTML-тэга выглядит на удивление просто:

<[^>]+>

Совпадает с любой последовательностью символов за исключением > в количестве от одного и более, заключённой в угловые скобки. Иными словами, будет найден и односимвольный тэг

И более "многословные" тэги, подобные


.

3.4. Вариант шаблона для поиска дат

Расширенные регулярные выражения позволяют написать несколько громоздкий, но тем не менее корректно работающий шаблон для поиска дат, имеющих вид "13 ноября 2009 г.":

? (янв|фев|мар|апр|мая|июн|июл|авг|сен|окт|ноя|дек).* г\.

Недостаток этого шаблона заключается в том, что с его помощью невозможно найти даты из древней истории, например, "13 ноября 245 г." или 1 января 88 г.", но для работы с современными документами он вполне годится (учитываем контекст поиска!).

3.5. Практическое применение нумерованных частей шаблона

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

\(.\)\(.\)\(.\)\3\2\1

С помощью такого шаблона можно находить шестисимвольные палиндромы не только на английском, но и на русском и на любых других языках, а также последовательности символов, не относящихся к алфавитным, например /*!!*/

Более практичным способом использования запомненных и пронумерованных частей шаблона является поиск стоящих рядом повторяющихся слов, что позволяет обнаружить такие часто встречающиеся в текстах ошибки (опечатки), как "для для". Шаблон можно записать так:

\<\(..*\)\> \<\1\>

Здесь применяются ещё два элемента регулярных выражений: \< для обозначения начальной границы слова и \> для обозначения конечной границы слова. Таким образом, мы запоминаем только отдельные слова, а не любые последовательности символов. Выражение..* соответствует любому слову, состоящему по крайней мере из одного символа. В результате мы сможем найти такие опечатки-повторения, как "и и", "не не", "для для" и т.п.

3.6. Ограничение размера совпадающей части шаблона

Ещё одна особенность "характера" регулярных выражений – они являются неимоверно "жадными" (greedy), т.е. стремятся обеспечить совпадение с как можно более длинной строкой. Из-за этой "жадности" могут возникать неожиданные проблемы. Например, имеется шаблон для поиска любого количества символов, заключённых в кавычки:

".*"

Строки, в которых производится поиск, имеют следующий вид:

"Петров" "охранник" "Иванов" "отдел снабжения" "экспедитор" "Сидоров" "администрация" "директор"

Если была поставлена задача извлечения из данных строк только первого аргумента (фамилия сотрудника), то предложенный выше шаблон выполнит её некорректно, поскольку вторая кавычка шаблона соответствует последней кавычке строки (из-за стремления получить максимальное совпадение). Изменение шаблона:

".*" ".*"

решает проблему только для первой строки, а во второй и третьей к фамилии подцепляется ещё и место работы – опять не то, что нам нужно!

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

"[^"]*"

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

4. Заключение

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

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

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

Ресурсы для скачивания

static.content.url=http://www.сайт/developerworks/js/artrating/

ArticleID=487264

ArticleTitle=Секреты регулярных выражений (regular expressions): Часть 1. Диалекты и возможности. Составление регулярных выражений