Что такое регулярные выражения, зачем они нужны?.. в этой статье я попробую просто и доступно объяснить, что это такое, не вдаваясь в сложные и непонятные подробности.. как показала практика обучения, понимание вещей у каждого своё, поэтому повествование буду вести исключительно на примерах.. :-)
Регулярные выражения (англ. regular expressions, regexp, regex) — это формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов. По сути это строка-образец (паттерн, шаблон, маска), состоящая из символов и метасимволов и задающая правило поиска.
Где они используются, спросите вы?.. в принципе много где: веб-серверы (Apache), серверы баз данных (Oracle, MySql, MsSql и т.п.), языки программирования (Perl, Php, Javascipt, C#, Java и т.п.), текстовые редакторы (MS Word и т.п.), поисковые машины (как локальные, так и интернет-поисковики) -- вобщем везде, где нужна быстрая обработка текста..
Синтаксис регулярных выражений везде один, различие только в именах функций вызова разборщика (парсера) регулярных выражений.. Поэтому будем изучать регулярные выражения применительно к php..
Например, есть строка 03-Jan-2009, являющейся англоязычной записью даты, и нам нужно из этой строки извлечь день, месяц и год. Используя регулярные выражения это сделать несложно (пример на php):
$A = "03-Jan-2009"; // исходная строка
if(preg_match("/^(\d+)-(\w+)-(\d{3,})$/i", $A, $p)){
// теперь в массиве $p у нас будет следующие данные
// $p[0] = 03-Jan-2009 -- полное соответствие паттерна (шаблона) исходной строке
// $p[1] = 03 -- часть исходной строки, соответствующая 1ой скобке паттерна
// $p[2] = Jan -- часть исходной строки, соответствующая 2ой скобке паттерна
// $p[3] = 2009 -- часть исходной строки, соответствующая 3ой скобке паттерна
}else{
// неверная строка
}
теперь подробнее.. выражение /^(\d+)-(\w+)-(\d{3,})$/i внутри функции preg_match называется паттерном (шаблоном, маской поиска): часть паттерна (точнее часть исходной строки, соответствующая части паттерна), заключенная в скобки (...) сохраняется как отдельный элемент массива -- первая скобка как 1ый элемент, 2ая скобка как 2ой элемент и т.д.
выражение \d указывает разборщику (парсеру) регулярных выражений, что в этом месте должны быть цифры.. это так называемые метасимволы, есть следующие их разновидности:
\d -- цифры,
\D -- не цифры,
\w -- буквы латинницы или символ подчеркивания,
\W -- не буквы латинницы или подчеркивание,
\s -- пробел, табуляция, переход на другую строку,
\S -- не пробел или табуляция, или переход на другую строку,
[ ] -- набор символов, например, [abcd] укажет набор из 4 символов a, b, c, d..
через символ - (минус) можно указывать диапазон символов, пример выше можно переписать как: [a-d]
для того чтобы в наборе указать символ минуса или скобок, нужно их экранировать слешем, например: [\-\[\]]
[^ ] -- исключающий набор символов, например, [^abcd] указывает набор всех символов ansi, исключая a, b, c, d..
. (точка) -- любой символ..
символ + в выражении \d+ означает что будет 1 или более символов отвечающих маске \d.. это так называемый квантификатор, он указывает сколько соответствий нужно искать, есть следующие разновидности:
+ -- одно или более совпадений,
? -- одно или ноль,
* -- ноль или любое количество,
{N, M} -- количество совпадений от N до M,
{N,} -- количество совпадений от N и более.. например выражение \d{3,} в нашем паттерне указывает, что подряд должно идти 3 или более символов-цифр..
{, M} -- количество совпадений от 0 до M,
{N} -- количество совпадений равное N ..
есть некоторые хитрые сочетания квантификаторов, о которых в кратце скажу ниже...
символ i в конце нашего паттерна указывает парсеру, что поиск нужно вести без учета регистра символов.. это так называемый модификатор, есть следующие его разновидности:
i -- без учета регистра,
s -- метасимвол . (точка) определяет также переходы на другую строку, без этого можификатора все паттерны рассматриваются в пределах одной строки,
u -- паттерны рассматриваются как записанные в кодировке utf-8..
p.s.: при использовании функции php preg_replace(...) доступен модификатор e указывающий, что в заменяющей строке присутствует php-код, который нужно выполнить.. но использование такого способа вызывает некоторые проблемы, потому такой способ крайне не рекомендуется... взамен этому есть адекватная функция preg_replace_callback(...)..
символ ^ в начале паттерна указывает, что поиск совпадений следует вести от начала исходной строки, а символ $ в конце паттерна указывает, что конец поиска совпадений должен совпадать с концом исходной строки.. в нашем случае мы указали, что наш паттерн должен полностью соответствовать исходной строке..
Например, паттерн /^\d+/ для строки 123абв456 найдет совпадение 123,
а паттерн /\d+$/ для той же строки найдет совпадение 456.. надеюсь суть ясна, да?..
В принципе основа составления регулярных выражений уже есть.. надеюсь это не показалось вам сложным, потому что сейчас начнутся настоящие регулярные выражения.. :-)
Внутренние модификаторы..
Например, /((?i)abc)def/ найдет совпадение со строкой Abcdef, но не со строкой AbcDef.. внутренний модификатор действует до конца ближайшего субпаттерна..
Альтернативные субпаттерны..
Например, /(abc|def)/ найдет совпадения в строках abc и def..
Незахватывающие субпаттерны..
Например, /(\w+)(?:\d+)/ найдет совпадение в строке abcd123, но в массиве совпадений будет лишь один элемент abcd, второй субпаттерн не будет сохранен..
Незахватывающие субпаттерны можно совмещать с внутренними модификаторами,
например, /(?i:abc)def/ будет эквивалентно /(?:(?i)abc)def/
p.s.: использование внутренних модификаторов не совсем оправдано, кроме как худшей читаемости кода они ничего не дают.. но это лишь мое имхо..
Ссылки на субпаттерны..
Например, /(m[ao])\\1/ найдет совпадение в строках mama, momo, но не в строках mamo или moma.. при выполнении паттерна значение первого субпаттерна, заключенного в скобки, сохраняется в буфер, и в дальнейшем оно может быть использовано для поиска соответствия..
Утверждения, обратные утверждения..
Например, /ma(?=[nm])/ найдет совпадение в строках man и mam.. вы спросите, а в чем отличие от паттерна вида /ma[nm]/ .. отличие будет в том, что в первом случае обработке подвергается лишь ma, а во втором случае все слово man, mam.. другими словами, утверждения не участвуют в разборе паттерна.. надеюсь, что кто-то понял.. :-)
Есть также отрицательные утверждения, например, /ma(?!m)/ найдет совпадения в строках mah, mag, mak и т.д., но не в строке mam..
Если утверждение нуно поставить перед искомым паттерном, то синтаксис немного другой:
Например, /(?<=[jm])am/ найдет совпадение в строках jam и mam..
Например, /(?<!m)am/ найдет совпадение в строках ham, jam, lam и т.д., но не в строке mam..
Условные субпаттерны..
В общем виде условные субпаттерны можно записать как:
(? (условие) субпаттерн1 | субпаттерн0 ) или вкратце (? (условие) субпаттерн1 )
при выполнении условия выполняется поиск соответствий по субпаттерну1, если условие не выполняется то идет поиск по субпаттерну0..
Например, есть паттерн /(\d+)([\s\.\-]+)(?(?=\d)(\d+)|(\w+))\\2(\d+)/ ... кстати как думаете, что делает этот паттерн?.. есть предположения?.. а паттерн этот разбирает строку даты.. найдет совпадение в строках 23.12.12, 23-12-2012, 23 Dec 2012, но не в строках 23 12-12..
надо отметить, что используя условный субпаттерн мы разделяем сохраняемые субпаттерны, например, 23-12-2012 вернет массив элементов:
Array(
[0] => 23-12-2012
[1] => 23
[2] => -
[3] => 12
[4] =>
[5] => 2012
)
тогда как 23 Dec 2012 вернет:
Array(
[0] => 23 Dec 2012
[1] => 23
[2] =>
[3] =>
[4] => Dec
[5] => 2012
)
На этом собственно заканчивается история о регулярных выражениях.. есть правда еще однократные и рекурсивные субпаттерны, но в силу малой применимости рассматривать их мы не будем..
Посмотреть жизненные примеры использования регулярных выражений можно в этой статье..