Тарифы Услуги Сим-карты

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

> Регулярные выражения в Perl

Регулярные выражения используются для нахождения шаблонов в строках. Например, для того, чтобы найти в телефонной книге конкретное имя, или,например, все имена, начинающиеся с буквы "a". Работа с регулярными выражениями является одной из самых мощных и полезных, и в тоже время самых сложных для понимания особенностей Perl. Надеемся, что после прочтения этой статьи вы поймете, насколько это мощный и удобный инструмент. Получив некоторый опыт вы сможете использовать эти возможности с большой пользой для себя.

Операторы

Для работы с регулярными выражениями в Perl используются три оператора
- оператор сравнения (matching - m//), оператор подстановки
(substitution s///) и оператор перевода (translation - tr///).

Все три оператора используют переменную $_ по умолчанию, поэтому
дальше, пока не будут представлены операции =~ и!~, будем
пользоваться ею.

Оператор сравнения проверяет, подходит ли проверяемое выражение под
шаблон, и возвращает значение 1, если это так, и значение 0 в
противном случае. Запись этого оператора состоит из буквы m,
разделителя (чаще всего это косая линия - /, но в принципе это может
быть почти любой символ), шаблона и еще одного разделителя (такого же,
как и первый:).

Оператор сравнения
$_ = ;
if (m/hello/) {
print "hello user\n";
}

if ($input{"siteurl"} =~ #http://#) {
print $input{"siteurl"};
}

В этом примере проверяется, есть ли в строке, полученной со
стандартного входа, слово "hello". Если это так (оператор m// вернет
значение 1), то на стандартный выход вернется фраза "hello user".

Примечание:вообще-то символ "m" является необязательным, поэтому
оператор из этого примера может выглядеть просто как /hello/.

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

Оператор подстановки
$_ = "My name is Fred";
# oh no, my name is Jonathan!
s/Fred/Jonathan/;

В этом примере в строке $_ все слова Fred будут изменены на Jonathan.

Оператор перевода также производит подстановку, но несколько другого
характера - он используется для замены отдельных символом некоторыми
другими (определенными) символами. Синтаксис этого оператора похож на
синтаксис оператора подстановки, с тем отличием, что во-первых он
очевидно начинается с букв tr, а между разделителями вставляются не
шаблоны, а группы символов, первая - исходные символы, вторая -
подстановочные, причем соответствующие символы должны стоять на
одинаковых позициях в своих группах - если вы хотите заменить,
например латинскую "m" на кириллическую "м", они должны стоять на
одинаковых местах: "m" - в первой группе символов, "м" - во второй.

Оператор перевода
$_ = "hi.there, my.name.is.jonathan,";
tr/.,/ !/;

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

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

Возможности каждого из этих операторов можно расширить при помощии
модификаторов. Модификаторы - это грубо говоря символы которые
дописываются к оператору (например, так - s/fred/Jonathan/i), говоря о
том, как ему нужно разбирать рабочее значение.

Модификаторы для оператора сравнения:

  • x - позволяет использовать расширенные регулярные выражения;

Модификаторы для оператора подстановки:

  • e - вычисляет подстановочное выражение перед подстановкой;
  • g - находит все найденные подстроки;
  • i - игнорирует регистр символов в строке;
  • m - рассматривает строку как многострочное значение;
  • s - рассматривает строку как однострочое значение;
  • x - позволяет использовать расширенные регулярные выражения.

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

$_ = "My name is Fred";
s/fred/Jonathan/i; # My name is Jonathan
s/jonathan/routine()/ie; # My name is

Операции =~ и!~

Операции =~ и!~ позволяют использовать с операторами m//, s/// и
tr/// любые переменные, а не только $_, которая используется этими
операторами по умолчанию.

Оператор =~ выполняет те же функции, что и оператор присваивания "="
(в случае использования с операторами s/// и tr///) и оператор
сравнения "eq" (при использовании с оператором m//).

Операция =~
$name = "my name is Fred";
$name =~ s/fred/Jonathan/ig;

$string = "hello world";
if ($string =~ /hello/i) {
print "helloworlded in this string.";
}

Аналогично, операция!~ используется так же как и операция "ne" (ее
написание подобно операции чисельного сравнения!=), используется
только с оператором сравнения и означает отрицание удовлетворения
шаблону.

Операция!~
$string = "good";
if ($string !~ /bad/) {
print "hey, it"s not too bad yet!";
}

Память

И напоследок - о возможности более удобно работать с результатами
обработки регулярных выражений, а именно о хранении их в отдельных
переменных. Такими переменными являются предопределенные $&, $`, $", и
набор переменных $1, $2, ..., $9.

Переменная $&

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

$string = "error 404."
$string =~ m/\d+/;

Переменные $` и $ "

Эти переменные служат для хранения фрагментов, которые не
удовлетворили шаблону, а именно подстрок, которые стоят до и после
результата соответственно. Другими словами, после операции, например,
сравнения, значение исходой строки разделяется на три части - часть,
которая подошла под шаблон, и фрагменты, которые идут перед ней и
после нее. Эти части и помещаются в переменные $&, $` и $"
соответственно. (Обратите внимание на то, что в первой переменной -
обратная кавычка, а во второй - прямая). Посмотрим на предыдущий
пример.

$string = "error 404."
$string =~ m/\d+/;

$number = $&; # $number содержит "404"
$before = $`; # $before содержит "error"
$after = $"; # $after содержит "."

Переменные $1..$9

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

$string = "this is to be uppercased";
$string =~ s/(upper\w+)/uc($1)/;
# $string = "this is to be UPPERCASED"

$string = "15 apples, 2 foos, 3 bars";
while ($string =~ m/(\d+) (\w+)/g) {
print "$2: $1\n";
}
# Выведет apples: 15
# foos: 2
# bars: 3

Николай Матковский ,
11.05.2006.

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

Для начала разберемся - что же такое регулярное выражение. По-английски пишется так - Regular Expression (отсюда часто встречается сокращение "regexp" и даже по-русски "регэксп"). Во-первых, не стоит искать смысл в самом термине - это дословный перевод с английского языка, который представляется слишком абстрактным. Но что бы понять по какому принципу работают регулярные выражения, нам и нужно именно что абстрагироваться на уровень предположений. Пример с поиском вхождения подстроки должен быть понятен всем. Но, на самом деле, хотя с помощью регулярных выражений можно легко найти любое вхождение, этот пример не раскрывает всей прелести регэкспов. Лучше вспомните как работает поиск файлов по шаблону (или по маске). Алгоритм подразумевает использование определенных символов (wildcards), которые позволяют как бы закрыть ту часть имени, которая для нас не имеет значения. Однако сами wildcards не используются в именах файлов (что делает алгоритм менее гибким). Так вот, поиск файлов по шаблону позволяет отобрать те имена файлов, которые удовлетворяют заданному условию. При этом, можно указать и точное имя, а можно в каком-то месте имени сделать предположение (с помощью все тех же wildcards). Так вот, регулярные выражения позволяют выполнять аналогичный поиск в пределах некоторой последовательности байт. Добавьте к этому возможность работы с различными частями образованной маски как с отдельными единицами и вы поймете прелесть регэкспов.

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

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

По частям и все сразу

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

Внутри регулярных выражений обитают несколько жадных, многоруких и любопытных существ, не познакомившись с которыми вы не сможете составлять регэкспы. Речь о квантификаторах, мнимых символах, классах и ссылках. Здесь ссылки - это ссылки на найденный текст. Это стандартное определение, но мне оно кажется немного не подходящим. Накопители или контейнеры более удачное определение, так как они фактически содержат в себе часть (или все) совпадения. Под классами подразумеваются наборы символов. Мнимые символы - это утверждения. То есть мнимый символ не является частью искомого значения, но, в нагрузку ко всему прочему, требует что бы выполнялось определенное условие. Квантификатор - это признак повторения шаблона.

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

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

$source = "Pupkin";

$source =~ /^Pupkin/; # Оператор вернет истину, так

# как в $source Pupkin с самого начала

$source = "Vasya Pupkin";

$source =~ /^Pupkin/; # А здесь уже будет ложь, так как перед

# Пупкиным стоит его имя.

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

$source = "Pupkin";

$source =~ /Pupkin/; # Оператор вернет истину, так

# как в $source Pupkin с самого начала

$source = "Vasya Pupkin";

$source =~ /Pupkin/; # Здесь то же будет истина, так как

# Пупкин в строке есть, хотя и не с начала.

# Но ведь и шаблон не требовал Пупкина в начале строки

Теперь понятно, что такое мнимые символы? Просто дополнительное условие, а не часть искомого.

Итак, вернемся к нашим баранам

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

И что мы видим? У нас два контейнера, разделенных s. s - это специальный символ, указывающий на любой символ из подмножества пробельных (пробел, табуляция, etc...) Уточню. То что у нас между контейнерами указывает на единичный пробельный символ. Мы подошли к самой важной основополагающей - в регулярном выражении (попросту шаблоне) любой символ соответствует самому себе, если только он не является метасимволом со специальным значением. Вот s как раз и относится к таким метасимволам. Признаюсь, что наш пример вообще сплошь и рядом состоит из метасимволов. Да, да, в нем нет ни одного символа, соответствующего самому себе.

Итак, что же мы выяснили? Мы выяснили, что будем искать нечто, состоящее из двух контейнеров, которые разделены между собой единичным пробельным символом. Теперь пора разобраться с содержимым контейнеров. Начнем с правого - он проще. Точка в регэкспе определяет любой символ, кроме символа новой строки (есть некоторые моменты, когда абсолютно любой). Надеюсь, что такое любой символ понятно? Это может быть "1","2","8","D","a","r" или "b" и так далее и тому подобное от кодов с нуля до самого 255.

Ну а теперь, позвольте представить вам... Символ * превращает предыдущую часть шаблона в маленькое прожорливое существо - квантификатор. Этот самый квантификатор сожрет все символы (так как у нас перед этим было указание точка - любой символ) до самого конца строки. Бесплатный сыр только в мышеловке, но квантификатор этого не знает. Мы не зря поместили его в контейнер. После того, как обработка регулярного выражения будет завершена у нас будет контейнер, в котором сохранится все то, что сожрал квантификатор. Так как у нас всего два контейнера, то это контейнер будет у нас под номером два. В последствии мы так и скажем perl - а ну, отдай нам содержимое второго контейнера. Вот так то.

Итак, чего мы достигли? Мы будем искать нечто, состоящее из двух контейнеров, разделенных единичным пробельным символом. Правый контейнер у нас будет содержать всю ту часть строки, которая находится после единичного пробельного символа. После выполнения регулярного выражения мы сможем использовать содержимое правого (ну и левого то же) контейнера по своему усмотрению. Вот такой вывод на данный момент.

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

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

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

Заметьте как удобно - мы не указываем все символы подряд. Мы просто определяем границы с помощью метасимвола - (это как бы даже и не совсем метасимвол, а только в данном случае). Вместо перечисления цифровых символов мы можем записать

Хотя для цифровых символов есть более эффективное решение - метасимвол d. Итак, у нас в левой части определен класс символов. Но какой-то интересный класс получается - вроде привязанный к началу строки. Нет, метасимвол ^ внутри класса указывает на отрицание символов класса. Это значит, что на месте этой части шаблона должен находиться любой символ, не входящий в состав класса. То есть, для примера

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

#!C:/per/bin/perl -w

reg("Vasya Pupkin");

reg(" Vasya Pupkin");

reg("Vasyattpupkin");

print "$1=$1n$2=$2nn" if $_ =~ /([^s]*)s(.*)/;

В результате получится

Теперь давайте разберемся почему и как. Первый тест однозначно попадает под шаблон: Vasya не состоит из пробельных символов, далее следует один пробельный символ (натурально пробел), а Pupkin составляет оставшуюся часть строки. Результат второго теста у нас какой то странный. Первый контейнер у нас оказался пуст, а второй почему то содержит всю строку без ведущего пробела. С чем это связано? Да с тем, что квантификатор * означает ноль или более символов. Так как первым в строке у нас пробельный символ, в правый контейнер, согласно условию, попадает ноль непробельных символов. Далее, пробел то не входит в состав контейнеров. Ну а второй контейнер жрет всю строку до конца. Третий вариант, я думаю, понятен. Я уже говорил, что каждый символ регулярного выражения соответствует единичному. И только квантификаторы позволяют кушать несколько символов одного класса. В шаблоне контейнеры разделены одиночным пробельным символом. В левый контейнер попадает Vasya. Самым законным образом первый пробельный символ (табуляция в примере) пропускается, а правый контейнер кушает все что осталось - в том числе и второй табулятор. Таким образом, получаем Пупкина с ведущей табуляцией.

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

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

Осталось выяснить, каким образом правильно интерпретировать значения второго теста. Во-первых нужно избавиться от привязки к началу строки (по моему этот спецсимвол уже успел потеряться в наших примерах:). Итак, шаблон должен обрабатывать ситуации, когда в начале строки может быть один или несколько пробельных символов. Ну это же элементарно, скажете вы, нужно просто добавить в начало шаблона s и сделать из него квантификатор.

/s*([^s]*)s*(.*)/

Поздравляю! Вы прошли вводный курс по регэкспам;)

Про обжору и другие тонкости

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

Смысл его очевиден - искать Пупкина перед которым может быть что то еще. Однако если источник содержит несколько Пупкиных, то квантификатор сожрет все вплоть до последнего Пупкина. Например поиск по этому регэкспу в строке

Vasya pupkin pupkin

приведет к тому, что квантификатор сожрет "Vasya pupkin ", а не "Vasya " как можно было ожидать. Для решения этой проблемы, достойной пристального внимания, имеется ряд специальных символов. Прежде всего символ вопроса? позволяет ограничить апетит квантификатора минимальной строкой совпадения. Возвращаясь к нашему примеру с несколькими Пупкиными получим

для корректного поедания "Vasya " из строки "Vasya pupkin pupkin". Далее, конструкции с фигурными скобками позволяют определять границы апетита квантификатора. Внутри фигурных скобок (естественно после самого квантификатора) может быть указано одно или два значения, перечисленных через запятую, которые соответственно определяют пределы жадности. Впомним про спецификатор *. Аналогичный ему + превращает шаблон в обжору, которого не удовлетворяет менее одного совпадения. То есть при использовании + условие отбора является истинным только когда имеются 1 и более совпадений. Заметьте, что верхний предел у нас неопределен и может быть опущен внутри конструкции с фигурными скобками. Если внутри фигурных скобок указать всего одно значение без запятых, то квантификатор сожрет только такую строку, в которой совпадений с шаблоном будет именно указанное количество.

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

Это регулярное выражение будет помещать в контейнер от двух до десяти символов строки. При чем, учитывая жадность, по возможности квантификатор будет вбирать наибольшую строку. То есть если строка длиной 10 или более символов, то в контейнер попадут именно 10, а не 2 и не 5 символов.

$1= Vasya Pupkin

В общем с квантификаторами можно еще много баловаться. Всего рассказать все равно не удасться. Тут только одно средство - практиковаться.

Далее на повестке дня такое понятие как альтернативные шаблоны. Это элементы регулярного выражения, которые позволяют определять несколько вариантов шаблона. Самый наглядный пример это определение протокола в строке URL

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

Есть еще одна фича, которая может вам пригодиться. Это, так называемые, дополнительные конструкции. Они позволяют выполнять проверку до или после текущего места в шаблоне, но при этом в сам шаблон не входят. Их описывать я не буду, так как это обычная справочная информация, которая имеется в любой книге по 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/

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

1. Введение

Пара слов для тех, кто не совсем в курсе, о чем идет речь. Вы видели когда-нибудь маски имен файлов — всякие там *.html, filename.{txt|csv} и тд? Так вот, регулярные выражения — это те же «маски», только более сложные. В умелых руках регулярные выражения могут быть невероятно мощным инструментом . Так или иначе они используются в 95% моих скриптов.

Многие небезосновательно считают, что регулярные выражения — это скорее самостоятельный язык программирования, чем часть какого-либо языка. Регулярные выражения есть в Perl, PHP, Python , JavaScript, конфигурационных файлах Apache… В зависимости от языка, могут иметь место небольшие различия в синтаксисе регулярных выражений, но основные идеи везде одни и те же.

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

if (preg_match ("//" , $text ) ) {
// в тексте есть цифры
} else {
// в тексте нет ни одной цифры
}

и такой — на Perl:

if ($text =~ // ) {
# в тексте есть цифры
} else {

}

делают одно и то же. Как не сложно догадаться по комментариям в коде, здесь идет проверка, содержит ли строка $text хотя бы одну цифру.

2. Простые примеры

Как всегда, учиться будем на примерах. Квадратные скобки в регулярных выражениях означают «здесь должен быть один из перечисленных символов». Например, приведенному выше выражению соответствует любая строка, содержащая хотя бы одну цифру. Аналогично, выражению соответствует любая строка, содержащая хотя бы одну из первых трех букв латинского алфавита. Чтобы обозначить любой символ, кроме заданных, используется запись [^abcdef] , то есть с символом крышки сразу за открывающейся квадратной скобкой.

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

if ($text =~ // ) {
# в тексте есть цифры
} else {
# в тексте нет ни одной цифры
}

И еще пара примеров:

if ($text =~ // ) {
# в тексте есть цифры и/или строчные буквы
# подходит: abc, ZZaZZ, ===17
# не подходит: EPIC FAIL, @^*!@#
}

if ($text =~ /[^0-9]/ ) {
# в тексте есть символы, отличные от цифр
# подходит: abc, 123abc456, 0x1111111111
# не подходит: 123, 123456, 9999999999
}

if ($text =~ // ) {
# в тексте есть буквы латинского алфавита
# подходит: ___Abba___, zyx
# не подходит: 0123, ^_^
}

if ($text =~ // ) {
# текст содержит цифры и буквы от A до F
# подходит: ***777***, DeadC0de, intel, 0_o
# не подходит: Xor, wiki
}

Усложним задачу. Теперь нам нужно проверить не просто наличие или отсутствие определенных символов, а соответствие строки определенному формату. Вот несколько простых примеров:

if ($text =~ /num=/ ) {
# подходит: num=1, some_num=000, bebenum=2(&^*
# не подходит: NUM=1, my_num=-1, num=abc
}

if ($text =~ // ) {
# подходит:
# zzzzzz
#
# не подходит:
#
#
}

Внимательный читатель поинтересуется, что это за знак плюса стоит в последнем регулярном выражении? Этот символ означает «один или более символов, указанных перед этим плюсом». Почти то же самое обозначает символ звездочка «от нуля до сколько угодно символов, указанных перед звездочкой». Например, выражению A+ будет соответствовать последовательность из одного и более символов A, а выражению * — любое количество цифр, в том числе и ни одной.

Иногда количество символов нужно задать точнее. Это можно сделать с помощью фигурных скобок . Например, выражению {8} соответствует любая последовательность из ровно восьми цифр, а выражению {3,8} — последовательность, содержащая от 3-х до 8-и символов латинского алфавита.

Число на второй позиции можно не указывать. То есть выражение {3,} также может иметь место. Оно означает «не менее трех строчных букв латинского алфавита». Выражение {0,} полностью аналогично звездочке, а {1,} — плюсу. Выражение {0,1} можно записать более коротко, используя знак вопроса .

Пример (не самый простой, зато интересный):

if ($text =~ // ) {
# подходит:
# dfgddfgdfg
#
# не подходит:
#
#
}

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

3. Как выдрать кусок строки?

Символ вертикальной черты (он же «пайп» или просто «палка») в регулярных выражениях означает «или». Например, выражению {20}|{25} соответствуют все строки, содержащие 20 символов латинского алфавита или 25 цифр подряд. Обычно этот символ используется совместно с круглыми скобками , предназначенных для группировки частей регулярного выражения. Пример:

if ($filename =~ /backup(19|20){2}-{2}-{2}/ ) {
# подходит: backup2011-04-01, backup1999-01-13
# не подходит: backup1873-12-12, backup2101-07-07
}

У круглых скобок есть еще одна функция. С их помощью можно выдирать куски соответствующих строк. В PHP результат сохраняется в переменную, указанную третьим аргументом функции preg_match . В Perl совпадения для 1-ой, 2-ой … 9-ой пары скобок сохраняются в переменные $1, $2, …, $9 . Но удобнее использовать такую конструкцию:

if (my ($y , $m , $d ) =
$filename =~ /backup({4})-({2})-({2})/ ) {
print ;
}

Спрашивается, под каким номером искать совпадение в возвращаемом массиве, если регулярное выражение содержит вложенные скобки? Все просто — совпадения возвращаются в том же порядке, в котором идут открывающиеся скобки. Пример:

my $filename = "./dumps/backup2011-04-01.tgz" ;
$filename =~ /backup((20|19){2})-({2})-({2})/ ;
print "$1, $2, $3, $4\n " ;
# выведет: 2011, 20, 04, 01

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

if (my ($y , $m , $d ) =
$filename =~ /backup((?:20|19){2})-({2})-({2})/ ) {
print "year = $y, month = $m, day = $d\n " ;
}

Также за круглыми скобками может следовать вопросительный знак, плюс или звездочка, означающие, что конструкция, указанная в скобках, необязательна, должна повторяться 1+ раз или должна повторяться 0+ раз соответственно. Использование фигурных скобок вслед за круглыми также допустимо.

4. Начало и конец строки

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

if ($text =~ /^*/ ) {
# текст, начинающийся с десятичной цифры
# подходит: 3, 801403, 6543bebebe
# не подходит: 0275, -123, abc11111
}

if ($text =~ /^0x{1,8}$/ ) {
# шестнадцатеричное число в C-нотации
# подходит: 0x5f3759df, 0xDEADBEEF
# не подходит: 0x1234xxx, xxx0x5678, xxx0x9ABCxxx
}

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

Примечание: Если кого-нибудь интересует, что это за «магические числа» 0x5f3759df и 0xDEADBEEF , обращайтесь к Википедии.

5. Специальные символы

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

if (my ($name ) = $arg =~ /^--name=(.+)$/ ) {
print "Hello, $name!\n " ;
}

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

<span > Text <em > text</ em > text</ span > Source: http://сайт/</ span >

Следующий код вернет нам не то, что хотелось бы:

# в регулярном выражении содержится слэш, поэтому
# приходится использовать вместо него другой ограничитель
(.*)#;
print $text ;
# выведет наиболее длинное совпадение:
# Text text textSource: http://сайт/

А вот что произойдет, если отключить жадный разбор (внимание на знак вопроса):

my ($text ) = $data =~ m #(.*?)#;
print $text ;
# выведет первое совпадение:
# Text text text

Да, следующие строки делают одно и то же:

# обычная запись...
$text =~ /({4})-({2})-({2})/ ;
# на самом деле - лишь сокращение оператора m//
$text =~ m/({4})-({2})-({2})/ ;
# вместо слэша можно использовать разные скобочки:
$text =~ m { ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) } ;
$text =~ m< ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) >;
$text =~ m [ ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) ] ;
$text =~ m (([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) ) ;
# или даже такие символы:
$text =~ m ! ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) !;
$text =~ m | ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) |;
$text =~ m #({4})-({2})-({2})#;
# а также крышку, кавычки, двоеточие, запятую, точку, ...

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

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

# экранированная обратным слэшем точка
# означает именно точку, а не "любой символ"
my ($ext ) = $fname =~ /\.(+)$/ ;
print "file name: $fname, extension: $ext\n " ;

Кроме того, обратный слэш используется в следующих обозначениях:

  • \t — обозначает символ табуляции (t ab)
  • \r и \n — символы возврата каретки (r eturn) и новой строки (n ew line)
  • \xNN — соответствует символу с ASCII кодом NN, например \x41 соответствует заглавной букве A латинского алфавита
  • \s — соответствует пробелу (s pace), табуляции, символу новой строки или символу возврата каретки
  • \d — означает любую цифру (d igit), а точнее — то, что считается цифрой в Юникоде (см слайд номер 102 в этой презентации)
  • \w — означает так называемое «слово» (w ord), аналог

В последних трех выражениях запись буквы в верхнем регистре означает отрицание. Например, \D соответствует выражению [^0-9] , \W — выражению [^0-9a-zA-Z_] , а \S — любому «не пробельному» символу.

Все эти «буквенные» выражения можно использовать внутри квадратных скобок. Например, выражение полностью эквивалентно .

Особого внимания заслуживают выражения \b и \B , означающие границу слова (в том же понимании «слова», как и в случае с \w ) и отсутствие границы слова соответственно. Например, выражению perl\b соответствует строка «perl rulez!», но не соответствует «perlmonk». С выражением perl\B все с точностью наоборот. Надеюсь, идея ясна.

И еще один пример:

# разбиваем полное имя файла на путь и имя
my ($path , $fname ) = $full_name =~ /^(.*)\/([^\/]+)$/ ;

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

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

Поведение регулярных выражений можно менять с помощью модификаторов. Например, как вы уже могли заметить, соответствие строки регулярному выражению проверяется с учетом регистра символов. Изменить это поведение можно с помощью модификатора #(.*?)#g;
# будьте осторожны при использовании /g в скалярном контексте
# подробности здесь: http://koorchik.blogspot.com/2011/07/perl-5.html
print "$_\n " for (@words ) ;

Как было сказано выше, точка обозначает любой символ, кроме символа новой строки . Изменить такое поведение можно с помощью модификатора /s :

# выдираем из HTML-файла содержимое статьи,
# которое может содержать далеко не одну и не две строчки
my ($article ) = $html =~ m #

(.*?)
#s;

Кстати, если в регулярном выражении нужно обозначить «любой символ» без использования модификатора /s , используйте выражение [\d\D] . Оно означает «любой символ, являющийся цифрой, или не являющийся цифрой», то есть вообще любой символ.

Наконец, ничто не мешает использовать несколько модификаторов одновременно:

# выдираем из HTML-файла все, что выделено жирным
my @words = $html =~ m #(.*?)#gi;
# сработает для , или даже

Дополнение: Еще один полезный модификатор — /o . Он означает «компилировать регулярное выражение только один раз». В некоторых случаях этот модификатор может существенно ускорить скрипт. Правда, я не уверен, что он поддерживается где-то, кроме как в Perl. За наводку спасибо товарищу

  1. Заменить множественные пробелы и нетекстовые символы на одиночные пробелы:

    $text = "Here is the text."
    $text =~ tr[\000-\040\177\377][\040]s;
    print $text;
    Here is the text.

  2. Сократить удвоенные, утроенные и т.д. буквы:

    $text = "Here is the texxxxxxt.";
    $text =~ tr/a-zA-Z/s;
    print $text;
    Here is the text.

  3. Пересчитать количество небуквенных символов:

    $xcount=($text =~ tr/A-Za-z//c);

  4. Обнулить восьмой бит символов, удалить нетекстовые символы:

    $text =- tr{\200-\377}{\000-\l77};
    $text =~ tr[\000-\037\177]d;

  5. Заменить нетекстовые и 8-битные символы на одиночный пробел:

    $text =~ tr/\021-\176/ /cs;

Поиск отдельных слов

Чтобы выделить слово, можно использовать метасимвол \S соответствующий символам, отличным от "пробельных":

$text = "Now is the time.";
$text =- /(\S+)/;
print ;
Now

Однако метасимвол \S соответствует также и символам, обычно не используемым для идентификаторов. Чтобы отобрать слова, составленные из латинских букв, цифр и символов подчеркивания, нужно использовать метасимвол \w:

$text = "Now is the time.";
$text =~ /(\w+)/;
print ;
Now

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

$text = "Now is the time.";
$text =~ /(+)/;
print ;
Now

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

$text = "How is the time.";
$text=~/\b(+)\b/;
print ;
Now

Привязка к началу строки

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

$line = ".Hello!";
if($line=~m/^\./){
print "Shouldn"t start a sentence with a period!\n";
}
Shouldn"t start a sentence with a period!

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

Привязка к концу строки

Чтобы привязать шаблон к концу строки, используется метасимвол (мнимый символ) $ . В нашем примере мы используем привязку шаблона к началу и к концу строки, чтобы убедиться, что пользователь ввел только слово "exit":

While(<>){
if(m/"exlt$/) {exit;}
}

Поиск чисел

$test = "Hello!";
if($text =~ /\D/){
print "It is not a number.\n";
}
It is not a number.

To же самое можно проделать, использовав метасимвол \d:

$text = "333";
if($text =~ /^\d+$/){
print "It is a number.\n";
}
It is a number.

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

$text= "3,1415926";
if($text =~ /^(\d+\.\d*|\d+)$/){
print "It is a number.\n";
}
It is a number.

Кроме того, при проверке можно учитывать тот факт, что перед числом может стоять как плюс, так и минус (или пустое место):

$text = "-2.7182";
if ($text =~ /^([+-]*\d+)(\.\d*|)$/) {
print "It is a number.\n";

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

$text = "+0.142857142857142857";
if ($text =~ /^(+|-|)\d+(\.\d*\)$/) {
print "It is a number.\n";
}
It is a number.

Альтернативные шаблоны, если они присутствуют, проверяются слева направо. Перебор вариантов обрывается, как только найдено соответствие между текстом и шаблоном. Поэтому, например, порядок альтернатив в шаблоне (\.\d*|) мог бы стать критичным, если бы не привязка к концу строки. Наконец, вот как можно произвести проверку того, что текст является шестна-дцатеричным числом без знака и остальных атрибутов:

$text = "1AO";
unless (ftext =~ m/^+$/) {
print "It is not a hex number, \n";
}

Проверка идентификаторов

С помощью метасимвола \w можно проверить, состоит ли текст только из букв, цифр и символов подчеркивания (это те символы, которые perl называет словесными (word characters)):

$text="abc";
if($text=~/^\w+$/){
print "Only word characters found. \n";
}
Only word characters found.

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

$text = "аbс";
if($text=~ /^+$/) { print "Only letter characters found.\n"; }
Qnly letter characters found.

Наконец, для проверки, что текст является идентификатором, то есть начинаетcя с буквы и содержит буквы, цифры и символы подчеркивания, можно испольpовать команду:

$text = "X125c";
if($text=~ /^\w+$/) {
print "This is identifier.\n";
}
This is identifier.

Как найти множественные совпадения

Для поиска нескольких вхождений шаблона можно использовать модификатор g . Следующий пример, который мы уже видели ранее, использует команду m/.../ с модификатором g для поиска всех входжений буквы x в тексте:

$text="Here is texxxxxt";
while($text=~m/x/g) {
print "Found another x.\n";
}
Found another x.
Found another x.
Found another x.
Found another x.
Found another x.

Модификатор g делает поиск глобальным. В данном (скалярном) контексте perl помнит, где он остановился в строке при предыдущем поиске. Следующий поиск продолжается с отложенной точки. Без модификатора g команда m/.../ будет упорно находить первое вхождение буквы х, и цикл будет продолжаться бесконечно.

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

$text = "Here is texxxxxt.";
$text =~ s/x/z/g;
print $text;
Here is tezzzzzt.

Без модификатора g команда s/.../.../ заменит только первую букву х. Команда s/.../.../ возвращает в качестве значения число сделанных подстановок, что может оказаться полезным:

$text= "Here is texxxxxt.";
print (text =~ s/x/z/g)
5

Поиск нечувствительных к регистру совпадений

Вы можете использовать модификатор i , чтобы сделать поиск нечувствительным к разнице между заглавными и строчными буквами. В следующем примере про-грамма повторяет на экране введенный пользователем текст до тех пор, пока не будет введено Q , или q (сокращение для QUIT или quit), после чего программа прекращает работу:

While(<>) {
chomp;
unless (/^q$/i){ print
} else {
exit;
}
}

Выделение подстроки

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

$record = "Product number:12345
Product type: printer
Product price: 5";
if($record=~/Product type:\s*(+)/i){
print "The product"s type Is^.\n";
}
product"s type is printer.

Вызов функций и вычисление выражений при подстановке текста

Используя для команды s/.../.../ модификатор е, вы тем самым показываете, что правый операнд (то есть подставляемый текст) - это то выражение perl, которое надо вычислить. Например, с помощью встроенной функции perl uc (uppercase) можно заменить все строчные буквы слов строки на заглавные:

$text = "Now is the time.";
$text=~ s/(\w+)/uc()/ge;
print $text;
NOW IS THE TIME.

Вместо функции uc($l) можно поместить произвольный код, включая вызовы программ.

Поиск n-го совпадения

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

$text = "Name:Anne Nanie:Burkart Name:Glaire Name: Dan";
while ($text =~ /Name: \s*(\w+)/g) {
++$match;
print "Match number $match is .\n";
}

Match number 1 is Anne
Match number 2 is Burkart
Match number 3 is Claire
Match number 4 is Dan

Этот пример можно переписать, используя цикл for:

$text = "Name:Anne Name:Burkart Name:Ciaire Name:Dan";
for ($match = 0;
$text =~ /Name:\s*(\w+)/g;
print "Match number ${\match} is .\n")
{}
Match nuwber 1 Is Anne
Match number 2 is Burkart
Match number 3 is Claire
Match number 4 is Dan

Если же вам требуется определить нужное совпадение не по номеру, а по содержанию (например, по первой букве имени пользователя), то вместо счетчика $match можно анализировать содержимое переменной, обновляемой при каждом найденном совпадении. Когда требуется не найти, а заменить второе или третье вхождение текста, можно применить ту же схему, использовав в качестве тела цикла выражение perl, вызываемое для вычисления заменяющей строки:

$text = "Name:Anne Name:Burkart Name:Claire Name:Dan";
$match =0;
$text =~ s/(Name:\s*(\w+))/ # начинается код perl
if (++$match == 2) { # увеличить счетчик
"Name:John ()" # вернуть новое значение
} else { }# оставить старое значение
/gex;
print $text;
Name:Anne Name:John (Burkart) Name:ClaireName:Dan

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

Как ограничить "жадность" квантификаторов

По умолчанию квантификаторы ведут себя как "жадные" объекты. Начиная с текущей позиции поиска, они захватывают самую длинную строку, которой может соответствовать регулярное выражение, стоящее перед квантификатором. Алгоритм перебора с возвратами, используемый perl, способен ограничивать аппетит квантификаторов, возвращаясь назад и уменьшая длину захваченной строки, если не удалось найти соответствия между текстом и шаблоном. Однако этот механизм не всегда работает так, как хотелось бы. Рассмотрим следующий пример. Мы хотим заменить текст "That is" текстом "That"s". Однако в силу "жадности" квантификатора регулярное выражение " .*is " сопоставляется фрагменту текста от начала строки и до последнего найденного "is":


$text =~ s/.*is/That"s/;
print $texts;
That"sn"t it?

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

  • *? - ноль или несколько совпадений,
  • +? - одно или несколько совпадений,
  • ?? - ноль совпадений или одно совпадение,
  • {n}? - ровно n совпадений,
  • {n,}? - по крайней мере n совпадений,
  • {n,m}? - совпадений по крайней мере n , но не более, чем m.

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

$text = "That is some text, isn"t it?";
$text =~ s/.*?is/That"s/;
print $texts;
That"s some text, isn"t it?

Как удалить ведущие и завершающие пробелы

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

$text = " Now is the time.";
$text =~ s/^\s+//;
print $texts;
Now is the time.

Чтобы отсечь "хвостовые" пробелы, годится команда:

$text = "Now is the time. ";
$text =~ s/\s+$//;
print $texts;
Now is the time.

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

Например в тексте нужно найти текст, находящийся между открывающим и закрывающим тегом:

$text="blah-blah";
if($text=~m!<()>(.*?)/\1!ig) {
print "\n";
}

найдет все слова, стоящие между тегами и .

В регулярных выражениях пристутствует своя семантика: быстрота, торопливость и возврат. Если квантификатор * совпадает во многих случаях, то в результате быдет выведен наибольший по длинне результат. Это жадность. Быстрота: поиск старается найти как можно быстрее. "Text"=~/m*/ , по смыслу символов m нет, но в результате будет возвращено значение 0 . Т.е. формально 0 и более символов.

$test="aaooee ooaao";
$test=~s/o*/e/;
print $test;
eaaooee ooaao

потому что 1 элемент сторки - 0 и более символов.

Если добавить квантификатор g , то результат будет таким:

Eaeaeeeeee eeaeaee

т.к строка содержит 13 мест, где может встречатся o , в том числе и пустых.

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

  • /i игнорировать регистр
  • /x игнорировать пропуски в шаблоне и разрешить комментарии.
  • /g модификатор разрешающий выполнение поиска/замены везде, где это возможно
  • /gc не сбрасывается позиция при неудачном поиске.
  • /s разрешается совпрадение. с \n , игнорируется $* .
  • /m разрешить совпадение ^ и $ для начала и конца строки во внутренних переводах строк
  • /o однократная компиляция
  • /e правая часть s/// представляет собой выполняемый код
  • /ee правая часть s/// выполняется, после чего возвращаемое значение интерпретируется снова.

при вызове use locаle учитываются локальные настройки. Модификатор /g может заполнить массив значений @nums = m/(\d+)/g; но это сработает для ненакладывающихся совпадений. Чтобы поймать совпадения нужно воспользоваться оператором?=... Если ширина = 0 , то механизм поиска остался на прежнем месте. Найденые данные остаются внутри скобок. Если есть модификатор /g , то текущая позиция остается прежней, но происходит перемещение на один символ вперед.

$numbers="123456789";
@one=$numbers=~/(\d\d\d)/g;
@two=$numbers=~/(?=(\d\d\d))/g;
print "@one \n";
print "@two \n";

Модификаторы m и s нужны для поиска последовательностей символов, содержащих перевод строки. При s точка совпадает с \n и игнорируется $* . m делает совпадающими ^ и $ до и после \n . e правая часть выполняется как программный код: perl -i -n -p -e "s/(.)/lc()/g" *.html приводит все литеры во всех файлах *.html текущей директории к нижнему регистру.

В данной главе описывается синтаксис регулярных выражений. Чаще всего в Perl они используются в операторах поиска и замены таких как s// , m/ , операторах связки =~ или != и т.д. Как правило все эти операторы имеют схожие опции такие как:

Обычно все эти опции обозначают как "/x". Их можно использовать даже внутри шаблонов, используя новую конструкцию (?...)

Регулярные выражения или шаблоны (pattern) то же самое, что и regexp процедуры в Unix. Выражения и синтаксис заимствованы из свободно распространяемых процедур V8 Генри Спенсера (Henry Spencer), там же они подробно и описаны.

В шаблонах используются следующие метасимволы (символы обозначающие группы других символов) часто называемые egrep - стандартом:

Метасимволы имеют модификаторы (пишутся после метасимвола):

Во все других случаях фигурные скобки считаются обычными (регулярными) символами. Таким образом "*" эквивалентна {0,} , "+" - {1,} и "?" - {0,1}. n и m не могут быть больше 65536.

По умолчанию действие метасимволов "жадно" (greedy). Совпадение распространяется столько раз, сколько возможно, не учитывая результат действия следующих метасимволов. Если вы хотите "уменьшить их аппетит", то используйте символ "?". Это не изменяет значение метасимволов, просто уменьшает распространение. Таким образом:

Шаблоны работают так же, как и двойные кавычки, поэтому в них можно использовать `\` - символы (бэкслэш-символы):

\t - символ табуляции
\n - новая строка
\r - перевод каретки
- перевод формата
\v - вертикальная табуляция
\a - звонок
\e - escape
\033 - восьмеричная запись символа
\x1A - шестнадцатеричная
\c[ - control символ
\l - нижний регистр следующего символа
\u - верхний регистр -//-
\L - все символы в нижнем регистре до \E
\U - в верхнем -//-
\E - ограничитель смены регистра
\Q - отмена действия как метасимвола

Дополнительно в Perl добавлены следующие метасимволы:

Обратите внимание, что все это "один" символ. Для обозначения последовательности применяйте модификаторы. Так:

Кроме того существуют мнимые метасимволы. Обозначающие не существующие символы в месте смены значения. Такие как:

Граница слова (\b) - это мнимая точка между символами \w и \W. Внутри класса символов "\b" обозначает символ backspace (стирания). Метасимволы \A и \Z - аналогичны "^" и "$", но если начало строки "^" и конец строки "$" действуют для каждой строки в многострочной строке, то \A и \Z обозначают начало и конец всей многострочной строки.

Если внутри шаблона применяется группировка (круглые скобки), то номер подстроки группы обозначается как "\цифра". Заметьте, что за шаблоном в пределах выражения или блока эти группы обозначаются как "$цифра". Кроме этого существуют дополнительные переменные:

Пример:

$s = "Один 1 два 2 и три 3"; if ($s =~ /(\d+)\D+(\d+)/) { print "$1\n"; # Результат "1" print "$2\n"; # "2" print "$+\n"; # "2" print "$&\n"; # "1 два 2" print "$`\n"; # "Один " print "$"\n"; # " и три 3" }

Perl версии 5 содержит дополнительные конструкции шаблонов:

Пример:

$s = "1+2-3*4"; if ($s =~ /(\d)(?=-)/) # Найти цифру за которой стоит "-" { print "$1\n"; # Результат "2" } else { print "ошибка поиска\n";} (?!шаблон) - "заглядывание" вперед по отрицанию.

Пример:

$s = "1+2-3*4"; if ($s =~ /(\d)(?!\+)/) # Найти цифру за которой не стоит "+" { print "$1\n"; # Результат "2" } else { print "ошибка поиска\n";}

(?ismx) - "внутренние" модификаторы. Удобно применять в шаблонах, где например нужно внутри шаблона указать модификатор.

Правила регулярного выражения. (regex)

  1. Любой символ обозначает себя самого, если это не метасимвол. Если вам нужно отменить действие метасимвола, то поставьте перед ним "\".
  2. Строка символов обозначает строку этих символов.
  3. Множество возможных символов (класс) заключается в квадратные скобки "", это значит, что в данном месте может стоять один из указанных в скобках символов. Если первый символ в скобках это "^" - значит ни один из указанных символов не может стоять в данном месте выражения. Внутри класса можно употреблять символ "-", обозначающий диапазон символов. Например, a-z - один из малых букв латинского алфавита, 0-9 - цифра и т.д.
  4. Все символы, включая специальные, можно обозначать с помощью "\" как в языке С.
  5. Альтернативные последовательности разделяются символом "|" Заметьте что внутри квадратных скобок это обычный символ.
  6. Внутри регулярного выражения можно указывать "подшаблоны" заключая их в круглые скобки и ссылаться на них как "\номер" Первая скобка обозначается как "\1".