Реферат: Регулярные выражения в perl
#!C:/per/bin/perl -w
use strict;
reg("Vasya Pupkin");
reg(" Vasya Pupkin");
reg("Vasya\t\tpupkin");
sub reg{
print "\$1=$1\n\$2=$2\n\n" if $_[0] =~ /([^\s]*)\s(.*)/;
}
В результате получится
$1=Vasya
$2=Pupkin
$1=
$2=Vasya Pupkin
$1=Vasya
$2= pupkin
Теперь давайте разберемся почему и как. Первый тест однозначно попадает под шаблон: Vasya не состоит из пробельных символов, далее следует один пробельный символ (натурально пробел), а Pupkin составляет оставшуюся часть строки. Результат второго теста у нас какой то странный. Первый контейнер у нас оказался пуст, а второй почему то содержит всю строку без ведущего пробела. С чем это связано? Да с тем, что квантификатор * означает ноль или более символов. Так как первым в строке у нас пробельный символ, в правый контейнер, согласно условию, попадает ноль непробельных символов. Далее, пробел то не входит в состав контейнеров. Ну а второй контейнер жрет всю строку до конца. Третий вариант, я думаю, понятен. Я уже говорил, что каждый символ регулярного выражения соответствует единичному. И только квантификаторы позволяют кушать несколько символов одного класса. В шаблоне контейнеры разделены одиночным пробельным символом. В левый контейнер попадает Vasya. Самым законным образом первый пробельный символ (табуляция в примере) пропускается, а правый контейнер кушает все что осталось - в том числе и второй табулятор. Таким образом, получаем Пупкина с ведущей табуляцией.
Наверное это не совсем тот результат, который мы хотели бы получить. Нафига нам ведущие пробелы. Ну вы же знаете достаточно, что бы превратить разделитель контейнеров в квантификатор. Ну так приступайте :)
/([^\s]*)\s*(.*)/
Теперь наше регулярное выражение будет пропускать между именем и фамилией все пробельные символы. Результат должен быть таким.
$1=Vasya
$2=Pupkin
$1=
$2=Vasya Pupkin
$1=Vasya
$2=pupkin
Осталось выяснить, каким образом правильно интерпретировать значения второго теста. Во-первых нужно избавиться от привязки к началу строки (по моему этот спецсимвол уже успел потеряться в наших примерах :). Итак, шаблон должен обрабатывать ситуации, когда в начале строки может быть один или несколько пробельных символов. Ну это же элементарно, скажете вы, нужно просто добавить в начало шаблона \s и сделать из него квантификатор.
/\s*([^\s]*)\s*(.*)/
Поздравляю! Вы прошли вводный курс по регэкспам ;)
Про обжору и другие тонкости
Теперь стоит поговорить о тонкостях, которые имеют место быть при составление регулярных выражений. Самое известное - это прожорливость квантификатора. Означает это следующее: квантификатор имеет привычку вбирать в себя максимальную строку, какую только может съесть. Для примера можно взять следующий шаблон
/.*pupkin/
Смысл его очевиден - искать Пупкина перед которым может быть что то еще. Однако если источник содержит несколько Пупкиных, то квантификатор сожрет все вплоть до последнего Пупкина. Например поиск по этому регэкспу в строке
Vasya pupkin pupkin
приведет к тому, что квантификатор сожрет "Vasya pupkin ", а не "Vasya " как можно было ожидать. Для решения этой проблемы, достойной пристального внимания, имеется ряд специальных символов. Прежде всего символ вопроса ? позволяет ограничить апетит квантификатора минимальной строкой совпадения. Возвращаясь к нашему примеру с несколькими Пупкиными получим
/.*?pupkin/
для корректного поедания "Vasya " из строки "Vasya pupkin pupkin". Далее, конструкции с фигурными скобками позволяют определять границы апетита квантификатора. Внутри фигурных скобок (естественно после самого квантификатора) может быть указано одно или два значения, перечисленных через запятую, которые соответственно определяют пределы жадности. Впомним про спецификатор *. Аналогичный ему + превращает шаблон в обжору, которого не удовлетворяет менее одного совпадения. То есть при использовании + условие отбора является истинным только когда имеются 1 и более совпадений. Заметьте, что верхний предел у нас неопределен и может быть опущен внутри конструкции с фигурными скобками. Если внутри фигурных скобок указать всего одно значение без запятых, то квантификатор сожрет только такую строку, в которой совпадений с шаблоном будет именно указанное количество.
Что бы вам не показалось что мы снова забираемся в теоретические дебри, напомню, что все то о чем мы сейчас говорим относится только к проверке условия на совпадение участка строки с шаблоном. Мало того, с квантификаторами это далеко не все тонкости. Существуют еще некоторые аспекты, такие как правила применения квантификаторов около границ контейнеров. Но с этим вам придется разбираться самостоятельно. В общем можно привести такой простой пример
/(.{2,10})/
Это регулярное выражение будет помещать в контейнер от двух до десяти символов строки. При чем, учитывая жадность, по возможности квантификатор будет вбирать наибольшую строку. То есть если строка длиной 10 или более символов, то в контейнер попадут именно 10, а не 2 и не 5 символов.
$1=Vasya Pupkin
$2=in
$1= Vasya Pupkin
$2=kin
$1=Vasya pupkin
$2=kin
В общем с квантификаторами можно еще много баловаться. Всего рассказать все равно не удасться. Тут только одно средство - практиковаться.
Далее на повестке дня такое понятие как альтернативные шаблоны. Это элементы регулярного выражения, которые позволяют определять несколько вариантов шаблона. Самый наглядный пример это определение протокола в строке URL
/^(http|ftp)/
Мнимый символ привязки к началу строки может быть помещен и внутри круглых скобок - результат от этого не меняется. Странно, ведь конструкция с круглыми скобками используется для определения алтернатив, ведь она же используется и для группировки в контейнер. Совершенно верно. Альтернативные шаблоны приводят к автоматическому возникновению нового контейнера. Здесь важно не облажаться и правильно определить номер контейнера при извлечении результатов. Контейнер, который был открыт ранее, имеет наименьший номер. Таким образом можно разобраться даже во вложенных контейнерах.
Есть еще одна фича, которая может вам пригодиться. Это, так называемые, дополнительные конструкции. Они позволяют выполнять проверку до или после текущего места в шаблоне, но при этом в сам шаблон не входят. Их описывать я не буду, так как это обычная справочная информация, которая имеется в любой книге по perl. Просто - что бы вы знали.
Ну и в качестве итога по курсу средней углубленности в регулярные выражения можно собрать все, что мы узнали в виде перечисления составных элементов регулярных выражений
одиночные символы (characters) - он и есть одиночный, чего его комментировать ;)
классы символов (character classes) - [], [^]
альтернативные шаблоны (alternative match patterns) - (X|X|X)
квантификаторы (quantifiers) - {}, ?, +, *
мнимые символы (assertions) - \s, ^, $, etc...
контейнеры (backreferences) - $1,$2,$x
дополнительные конструкции
От теории к практике
В perl имеются три основных оператора которые работают со строками. Это
m// - проверка совпадений (или поиск)
s/// - подстановка
tr/// - замена
Каждый оператор имеет свои свои модификаторы. Для начала рассмотрим для чего нужны все три оператора.
Первый - m// (или просто //) используется для поиска совпадений с указанным шаблоном. Это как раз то, на чем мы тренировались выше. Там же и пример, как можно его использовать. Второй оператор s/// позволяет не только находить определенные участки, совпадающие с заданным шаблоном, но и выполнять неравнозначную подстановку. Фактически, s/// это то же что и m// (даже модификаторы совпадают), но с возможностью произвольной подстановки. Смысл неравнозначной подстановки открывается когда мы обращаемся к третьему оператору tr///. Оператор замены может заменять участки только на равнозначные по длине. Как следствие - он работает быстрее s///. Из всех операторов s/// самый гибкий - он позволяет выполнять все то, что могут m// и tr///. С его помощью можно свернуть горы. Но, за все приходится платить и здесь мы расплачиваемся скоростью. tr/// можно вообще не рассматривать (если конечо вы не фанат скорости). А вот на s/// хочется остановиться поподробнее.
Прежде всего хочу предупредить - не пытайтесь запихать в правую часть оператора s/// (то есть в ту, которая определяет что будем подставлять вместо найденного шаблона) квантификаторы, мнимые символы и вообще всякие другие неопределенности. Все должно быть четко и однозначно. Работа оператора s/// (в прочем как и m///) подразумевает компиляцию на каждом этапе обращения к регулярному выражению. Если вы не ленились (да и так он часто встречается) то уже знаете про модификатор глобального поиска g, который заставляет работать регэксп на протяжении остатка от предыдущего результата и так до конца строки. Так вот, если в правой части разместить имя переменной-контейнера и заюзать регэксп с модификаторами o и g, то наверняка выйдет бардак, так как o запрещает повторную компиляцию шаблона. В общем тут нужно быть предельно внимательным. Еще хочу обратить ваше внимание на модификаторы e и ee. Они позволяют выполнять код непосредственно в процессе работы регулярного выражения. Если у вас очень сложное задание и его очень трудно реализовать в одном регулярном выражении, разбейте их на составные в правой части - и работать будет быстрее и отлаживать проще.
Список литературы
Для подготовки данной работы были использованы материалы с сайта http://prolib.ru/