Личный кабинет        15.07.2019   

WDH: PERL - Регулярные выражения. Регулярные выражения (шаблоны)

В этой главе:

    Основные понятия
    Образцы
    Еще об операции сопоставления
    Операция замены
    Функции split и join
    Упражнения

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

Основные понятия

Регулярное выражение представляет собой образец

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

Регулярные выражения используются многими программами, в частности, UNIX-командами, программами

grep, sed, awk, ed, vi, emacs и даже различными shell. В каждой программе используется свой набор метасимволов (большей частью они совпадают). Perl - семантическое надмножество всех этих средств: любое регулярное выражение, которое можно записать в одной из подобных программ, может быть записано и на языке Perl, но не обязательно теми же символами.

Основные направления использования регулярных выражений

Если бы нам нужно было найти в каком-то файле все строки, содержащие строку

abc, мы могли бы использовать команду grep:

grep abc somefile >results

В этом случае

abc - регулярное выражение, которое команда grep сверяет с каждой входной строкой. Строки, соответствующие этому регулярному выражению, посылаются на стандартный вывод и попадают в файл results (так как в командной строке стоит оператор переадресации). Perl мы можем превратить строку abc в регулярное выражение, заключив ее между косыми:

if (/abc/) (print $_;

Но что же сверяется с регулярным выражением

abc в данном случае? Да наша старая подруга, переменная $_! Если регулярное выражение заключено между косыми (как в этом примере), то переменная $_ сверяется с регулярным выражением. Если значение переменной совпадает с регулярным выражением, операция сопоставления возвращает значение "истина". В противном случае она возвращает "ложь".

В данном примере предполагается, что переменная

$_ содержит какую-то строку текста и выводится, если в любом месте этой строки обнаруживается последовательность символов abc (аналогичные действия производит приведенная выше команда grep. Однако в отличие от grep, которая оперирует всеми строками файла, данный фрагмент Perl-программы просматривает только одну строку). Чтобы обрабатывались все строки, добавьте операцию цикла: (о) (

if (/abc/) { print $_;

А что, если мы не знаем, сколько символов

b стоит между а и с? То есть что нужно делать, если мы хотим вывести на экран строку только в том случае, если она содержит символ а, за которым следует ни одного или более символов b и символ с? Работая с grep, мы написали бы так:

grep "ab*c" somefile >results

(Аргумент, содержащий звездочку, заключен в кавычки, потому что мы не хотим, чтобы

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

if (/ab*c/) (print $_;

grep, такая запись обозначает последовательность, содержащую символ а, ни одного или более символов b и символ с.

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

Еще одна простая операция, в которой используются регулярные выражения,

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

Переменная (в данном случае

$_) сопоставляется с регулярным выражением (ab*c). Если сопоставление оказалось успешным, то соответствующая часть строки отбрасывается и заменяется строкой (def). Если сопоставление неудачно, ничего не происходит.

Позже, в разделе "Операция замены" , мы рассмотрим множество опций операции замены.

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

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

Образцы, обозначающие один символ

Самый простой и самый распространенный символ, встречающийся в регулярных выражениях,

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

Следующий из самых известных символов сопоставления

- точка ("."). Точка обозначает любой одиночный символ, кроме символа новой строки (\п). Например, образцу /а . / соответствует любая двухбуквенная последовательность, которая начинается с буквы а и не является последовательностью "а\п".

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

соответствует строка, содержащая любую из первых пяти строчных букв алфавита, тогда как образцу

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

(]), поставьте перед ней обратную косую или же поставьте эту скобку на первое место в списке. Диапазоны символов (например, от а до z) можно приводить в сокращенной записи, указав конечные точки диапазона через дефис (-). Чтобы включить в список дефис как таковой, поставьте перед ним обратную косую или поместите его в конец. Вот еще несколько примеров: # обозначает любую цифру # то же самое

# обозначает цифры

0-9 или знак минус обозначает любую строчную букву или цифру обозначает любую букву, цифру или знак подчеркивания

Существует также такое понятие, как отрицание класса символов: оно обозначается знаком л, который ставится сразу же за левой скобкой. Такому классу символов соответствует любой символ, отсутствующий в этом списке. Например:

# обозначает любой нецифровой символ обозначает любую негласную букву # обозначает любой символ, кроме символа "

Для удобства пользователя некоторые распространенные классы символов определены заранее. Они представлены в таблице

7.1. 7.1. Предопределенные классы символов \d соответствует одна цифра. Образцу \w формально соответствует один обычный символ, но на самом деле ему соответствует любой символ, который допустим в именах переменных Perl. Образцу \s соответствует один пробельный символ. К пробельным символам относятся пробел, возврат каретки (редко используемый в UNIX), символ табуляции, символы перехода на новую строку и на новую страницу. Варианты конструкций с "использованием прописных букв соответствуют дополнениям (отрицаниям) этих классов. Так, \w обозначает один специальный символ, \s - один символ, который не является пробельным (т.е. является буквой, знаком препинания, управляющим символом и т.д.), a \D - один нецифровой символ.

Приведенные выше конструкции можно использовать при задании других классов символов:

соответствует одной шестнадцатеричной цифре

Образцы, обозначающие группу символов

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

Последовательность

Первый (и, вероятно, самый неочевидный) образец данного вида

- последовательность. Например, образец abc соответствует букве а, за которой следует буква Ь, за которой идет буква с. Вроде бы просто, но название этому виду образца все равно нужно дать, чтобы в дальнейшем знать, о чем идет речь.

Множители

Мы уже встречались со звездочкой

(*) в роли образца, обозначающего группу символов. Звездочка обозначает ни одного или более экземпляров стоящего непосредственно перед ней символа (или класса символов).

Есть еще два образца, работающих подобным образом: знак "плюс"

(+), который обозначает один или более экземпляров стоящего непосредственно перед ним символа, и вопросительный знак (?), который обозначает ни одного или один экземпляр стоящего непосредственно перед ним символа. Например, регулярное выражение /fo+ba?r/ обозначает символ f, за которым следует один или более символов о, затем символ Ь, затем ни одного или один символ а и, наконец, символ г.

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

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

$_ = "fred xxxxxxxxxx barney";

всегда заменяет словом

boom все символы х (что в результате дает fred boom barney), а не только один или два, несмотря на то, что более короткий набор иксов соответствовал бы этому же регулярному выражению.

Если нужно сказать "от пяти до десяти" символов х, можно поставить пять иксов, а затем еще пять, дав после каждого из последних пяти вопросительный знак. Это, однако, выглядит уродливо. Есть более простой способ

- применение общего множителя. Общий множитель состоит из пары фигурных скобок, между которыми заключены одно-два числа, например /х{5,10}. Необходимо найти символ, стоящий непосредственно перед скобками (в данном случае это буква х), повторяющийся указанное число раз (в рассматриваемом случае - от пяти до десяти)*.

Если второе число не указано (например, /х

{5, } /), это означает "столько или больше" (в данном случае пять и более), а если выпущена и запятая (например, /х{5}/) , это означает "ровно столько" (в данном случае пять символов х). Чтобы получить пять или менее символов х, нужно перед запятой поставить нуль: /х {0, 5} /.

Так, регулярное выражение /а

. {5} b/ соответствует букве а, отделенной от буквы b любыми пятью символами, кроме символов новой строки, и все это может быть в любом месте строки. (Вспомните, что точка соответствует любому символу, кроме символа новой строки, а нам здесь нужно пять таких символов.) Эти пять символов не обязательно должны быть одинаковыми. (В следующем разделе мы увидим, как заставить их быть одинаковыми.)

Можно было бы вполне обойтись без

*, + и ?, потому что эти образцы полностью эквивалентны образцам {0,},(!,} и {0,1}, но проще ввести один эквивалентный знак препинания, к тому же это более привычно.

Если в одном выражении используются два множителя, то "правило прожорливости" дополняется правилом "чем левее, тем прожорливее". Например:

"а ххх с хххххххх с ххх d";

В этом случае первая комбинация

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

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

"а ххх с хххххххх с ххх d"; . * ? с теперь соответствует минимальному числу символов между а и с, а не максимальному. Это значит, что с образцом совпадает часть Конечно, /\d(3}/ соответствует не только трехзначным числам, но и любому числу с количеством знаков больше трех. Чтобы задать именно трехзначное число, нужно использовать фиксирующие точки, которые рассматриваются ниже в разделе "Фиксирующие образцы".

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

(?,+,* и {m,n}).

Что, если строка и регулярное выражение несколько изменятся, скажем, так:

° "а ххх се хххххххх ci xxx d"; .* в этом случае соответствуют максимально возможному числу символов, стоящих до следующей буквы с, но очередной символ регулярного выражения (е) не совпадает с очередным символом строки (i). В этом случае мы получаем автоматический поиск с возвратом: поиск начинается сначала и завершается остановкой в некоторой позиции до выбранной на первом этапе (в нашем случае - в позиции предыдущей с, рядом с е)*. Сложное регулярное выражение может включать множество уровней поиска с возвратом, в результате чего время выполнения значительно увеличивается. В данном случае превращение множителя в "ленивый" (с помощью вопросительного знака) упрощает задачу, которую должен выполнить Perl, поэтому рекомендуем хорошо изучить этот метод.

Круглые скобки как способ запоминания

Следующая групповая операция

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

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

/fred(.)barney\l/;

соответствует строке, состоящей из слова

fred, любого символа, кроме символа новой строки, слова barney и еще одного такого же символа. Таким образом, данному образцу соответствует последовательность символов fredxbarneyx, a не fredxbarneyy. Сравните это с

где два обозначенных точками символа могут быть одинаковыми или разными; роли это не играет.

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

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

то вторая часть (считая левые круглые скобки слева направо) обозначается как \2, третья

- как \3 и т. д. Например,

/a(.)b(.)c\2d\l/;

обозначает а, какой-то символ (назовем его

#1), b, еще один символ (назовем его #2), с, символ #2, d и символ #1. Таким образом, этот образец соответствует, в частности, строке axbycydx.

Запоминаемая часть может состоять не только из одного символа. Например,

обозначает а, любое количество символов (даже нуль),

b, ту же последовательность символов и, наконец, с. Следовательно, этот образец совпадет со строкой aFREDbFREDc и даже со строкой abc, но не со строкой аХХЬХХХс.

Дизъюнкция

Следующая групповая конструкция

- дизъюнкция, т.е. а | b | с. Это значит, что данный образец соответствует только одному из указанных вариантов (в данном случае - а, b или с). Такая конструкция работает даже в том случае, если варианты содержат несколько символов, как в образце /song | blue/, что соответствует либо song, либо blue. (Для односимвольных альтернатив определенно лучше будет использовать класс символов, например, / [ abc ] /.)

Что, если бы мы хотели найти

songbird или bluebird? Мы могли бы написать /songbird | bluebird/, но часть bird не хотелось бы указывать дважды. Из такой ситуации есть выход, однако вначале нам следует поговорить о приоритете группирующих образцов, который рассматривается ниже, в разделе "Приоритет".

Фиксирование образцов

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

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

b происходило только на границе слова. Граница слова - это место между символами, которые соответствуют предопределенным классам \w или \w, либо между символами, которые соответствуют классу \w, а также начало или окончание строки. Отметим, что все это больше предназначено для работы с С, а не с английскими словами, но вполне применимо и к словам. Например: соответствует слову fred, но не Frederick /\bmo/; # соответствует словам тое и mole, но не Eimo /\bFred\b/; # соответствует слову Fred, но не Frederick или alFred /\b\+\b/; # соответствует "х+у", но не "++" или " + " /abc/bdef/; # никогда не дает совпадения(границы там быть не может)

Аналогичным образом \в требует, чтобы в указанной точке границы слова не было. Например:

соответствует "Frederick", но не "Fred Flintstone"

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

- первый символ в строке, a л соответствует двум символам, а и л, стоящим в любом месте строки. Другими словами, символ л утратил свое специальное значение. Если вы хотите, чтобы он имел буквальный смысл и в начале строки, поставьте перед ним обратную косую черту. $, как и л , фиксирует образец, но не по началу, а по концу строки. Другими словами, с$ соответствует символу с только в том случае, если он стоит в конце строки*. Знак доллара в любом другом месте образца, вероятно, будет интерпретироваться как представление скалярного значения, поэтому для того, чтобы использовать его в строке буквально, перед ним следует поставить обратную косую.

Поддерживаются и другие фиксирующие точки, включая \А, \2 и упреждающие фиксирующие точки, создаваемые с помощью комбинаций

(?=...) и (?!...). Они подробно описаны в главе 2 книги Programming Perl и на man-странице perlre(Y).

Приоритет

Что произойдет, если объединить а | Ь*? Что будет отыскиваться

- любое количество символов а или Ь или один символ а и любое количество Ь?

Групповые и фиксированные образцы, как и операции, имеют приоритет. Приоритет образцов (от высшего к низшему) приведен в таблице

7.2. 7.2. Приоритет групповых регулярных выражений** Или прямо перед символом новой строки в конце строки. Некоторые из этих символов в нашей книге не описываются. См. книгу Programming Perl или man-страницу perlreii(l).

Согласно этой таблице, специальный символ

* имеет более высокий приоритет, чем | . В силу этого /а |Ь*/ интерпретируется как один символ а или любое число символов ь.

Что, если нам понадобится другое

- например, "любое число символов а или Ь"? В этом случае нужно просто использовать пару круглых скобок. В нашем примере в скобки нужно заключить ту часть выражения, к которой должна относиться *, т.е. (а|Ь)*. Если вы хотите подчеркнуть, какое выражение вычисляется первым, можно дать избыточные круглые скобки:

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

(?:...), а не (...). Она тоже позволяет указывать множители, но не изменяет значение счетчика подлежащих запоминанию лексем, используя, например, переменную $4 и т.п. Например, /(?: Fred |Wilma) Flintstone/ ничего не записывает в переменную $ 1; здесь просто предполагается группирование.

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

соответствует ab, abc, abcc, abccc, abcccc, и т.д. (abc)* # соответствует "", ab, abc, abcabc, abcabcabc, и т.д. ^х |у # соответствует х в начале строки или у в любом месте л ^x.^y) # соответствует х или у в начале строки а| be Id # либо а, либо be, либо d (alb) (с Id) # ас, ad, be или bd (song|blue)bird # songbird или bluebird

Еще об операции сопоставления

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

Выбор другого объекта для сопоставления (операция

: =" )

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

$_, и помещать ее туда довольно утомительно. (Может быть, в переменной $__ уже хранится значение, которое вам не хочется терять.) Ничего страшного - здесь нам поможет операция =~. С ее помощью вы можете назначить для проведения операции сопоставления строку, хранящуюся в переменной, отличной от $_.

Эта переменная указывается справа от знака операции. Выглядит это так:

= "hello world"; /^he/; # истина

$а =~ /(.)\1/; # тоже истина (соответствует двум

1) ($а =~ /(.)\1/) (t истина, поэтому проводятся дальнейшие операции

Справа от знака операции =~ может стоять любое выражение, которое дает в результате некоторое скалярное строковое значение. Например,

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

print "any last request? ";

if ( ==~ /

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

Игнорирование регистра

В предыдущем примере мы указывали образец

для обозначения строчной и прописной буквы у. Если речь идет об очень коротких строках, например, у или fred, то данный способ обозначения достаточно удобен, скажем, . А что делать, если сопоставляемая строка - это слово procedure в нижнем или верхнем регистре?

В некоторых версиях

grep флаг -i означает "игнорировать регистр". В Perl тоже есть такая опция. Чтобы ею воспользоваться, нужно добавить строчную i к закрывающей косой черте, т.е. написать / образец/ i. Такая запись говорит о том, что буквы образца будут соответствовать буквам строки в любом регистре. Например, чтобы найти слово procedure в любом регистре, стоящее в начале строки, запишите /^procedure/i.

Теперь наш предыдущий пример будет выглядеть так:

print "any last request? ";

if ( =~ /"y/i) { #

начинаются ли входные данные с буквы у? # да ! выполнить какие-то операции

Использование другого разделителя

Чтобы найти строку, которая содержит несколько косых

(/), в соответствующем регулярном выражении нужно перед каждой из них поставить обратную косую черту (\). Например, чтобы найти строку, которая начинается с названия директории /usr/etc, нужно записать: /usr/etc... }

Как видите, комбинация "обратная косая

- косая" создает между элементами текста своеобразные "проходы". Если косых очень много, это занятие может стать весьма утомительным, поэтому в Perl предусмотрена возможность использования другого разделителя (delimiter). Поставьте перед любым специальным символом* (выбранным вами в качестве разделителя) букву т, укажите свой образец и дайте еще один такой же разделитель: использование стандартного разделителя - косой черты m@^/usr/etc@ # использование в качестве разделителя символа @ m#^/usr/etc# # использование в качестве разделителя символа # # (это мой любимый символ)

Если хотите, можете опять использовать косые, например,

m/fred/. Таким образом, m - общепринятое обозначение операции сопоставления с регулярным выражением, но если в качестве разделителя выбрана косая черта, то m не обязательна.

Использование интерполяции переменных

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

if ($sentence =~ /\b$what\b/) {

print "The sentence contains the word $what!\n";

\bbird\b/. Если этот разделитель - левый элемент пары (круглая, фигурная, угловая или квадратная скобка), то закрывающим разделителем будет соответствующий правый элемент пары. В остальных случаях первый и второй разделители будут совпадать.

Вот несколько более сложный пример:

$sentence = "Every good bird does fly.";

print "What should I look for? ";

$what = ;

if ($sentence =~ /$what/) (#

нашли! print "I saw $what in $sentence.\n";

print "nope... didn"t find it.\n";

Если вы введете слово

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

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

\Q:

$what = "";

foreach (qw(in( out white )) { if (/\Q$what\E/) {

print "$_ matched!\n";

Здесь конструкция

\Q$what\E превращается в \, в результате чего операция сопоставления ищет пару квадратных скобок, а не рассматривает всю конструкцию как класс символов.

Специальные переменные, защищенные от записи

После успешного сопоставления с образцом переменным

$1, $2, $3 и т.д. присваиваются те же значения, что и \1, \2,\3 и т.д. Это можно использовать для поиска соответствия в последующем коде. Например:

$_ = "this is a test";

/(\w+)\W+(\w+)/; #

сопоставление первых двух слов теперь содержит this, а $2 - is

Доступ к тем же значениям

($1, $2, $3 и т.д.) можно также получить, использовав операцию сопоставления для соответствующих списков. Если результаты сопоставления окажутся положительными, будет получен список значений от $1 до $п (где n - количество занесенных в память элементов). В противном случае значения не определены. Запишем последний пример по-другому:

$_ = "this is a test";

($first, $second) = /(\w+)\W+(\w+)/; #

сопоставление первых двух слов # $first теперь содержит this, a $second - is

К другим предопределенным защищенным от записи переменным относятся:

$& (часть строки, совпавшая с регулярным выражением); $" (часть строки, стоящая перед совпавшей частью); $ " (часть строки, стоящая после совпавшей части). Например:

$_ = "this is a sample string";

соответствует слову sample внутри строки теперь содержит "this is a " теперь содержит "sample" теперь содержит "string"

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

Операция замены

Мы уже говорили о простейшей форме операции замены:

s/ регуляр-ное_выражение/новая_строка/. Пора рассмотреть несколько разновидностей этой операции.

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

$_ = "foot fool buffoon";

s/foo/bar/g; # $_

теперь содержит "bart barl bufbarn"

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

$_ = "hello, world";

$new = "goodbye";

s/hello/$new/; #

заменяет hello на goodbye

Символы сопоставления (метасимволы) в регулярном выражении позволяют выполнять сопоставление с образцом, а не просто с символами, трактуемыми буквально:

$_ = "this is a test";

s/(\w+()/<$l>/g; # $_

теперь содержит " "

Вспомните, что в

$1 заносятся данные, полученные при совпадении с первой заключенной в круглые скобки частью образца. i (перед буквой g или после нее, если она есть) заставляет используемое в операции замены регулярное выражение игнорировать регистр, как и аналогичная опция в ранее рассмотренной нами операции сопоставления. О влиянии этих переменных на производительность рассказывается в книге Mastering Regular Expressions (издательство O"Reilly). ; Как и в операции сопоставления, можно выбрать другой разделитель, если косая черта неудобна. Для этого просто нужно использовать один символ три раза*: # заменить fred на barney, как в s/fred/barney/

Как и при сопоставлении, можно с помощью операции =~ указать другой объект для проведения замены. В этом случае объект должен быть таким, которому можно присвоить скалярное значение,

- например, скалярной переменной или элементом массива:

$which = "this is a test";

$which =~ s/test/quiz/; # $which

теперь содержит "this is a quiz"

$someplace[$here] =~ s/left/right/; #

заменить элемент массива

$d{"t") =~ s/^/x /; #

поставить "х " перед элементом массива split и join

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

split. Функция join выполняет противоположное действие - вновь "склеивает" эти кусочки. split split получает регулярное выражение и строку и ищет в этой строке все экземпляры указанного регулярного выражения. Те части строки, которые не совпадают с регулярным выражением, возвращаются по порядку как список значений. Вот, например, код синтаксического анализа разделенных двоеточиями полей, аналогичных тем, которые используются в UNIX-файлах /etc/passwd:

$line = "merlyn::118:10:Randal:/home/merlyn:/usr/bin/peri";

@fields = split (/:/,$line); #

разбить $line, используя в качестве t разделителя двоеточие @fields содержит ("merlyn","","118","10",

# "Randal","/home/merlyn","/usr/bin/peri")

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

Sfields = split(/:+/, $line);

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

Очень часто приходится разбивать на поля значение переменной

$_, поэтому этот случай предлагается по умолчанию:

$ = "some string";

Swords = split (/ /); #

то же самое, что и Swords = split(/ /, $_); * Или две пары, если используется символ из пары "левая-правая".

При такой разбивке соседние пробелы в разбиваемой строке вызовут появление пустых полей (пустых строк). Лучше использовать образец

/ +/, а лучше /\s+/, который соответствует одному и более пробельным символам. Этот образец, по сути дела, используется по умолчанию*, поэтому, если вы разбиваете переменную $_ по пробельным символам, вы можете использовать все стандартные значения и просто написать :

Swords = split; #

то же самое, что и (Swords = split(/\s+/, $_) ;

Завершающие строки пустые поля в список, как правило, не включаются. Особой роли это обычно не играет. Решение вроде

$line = "merlyn::118:10:Randal:/home/merlyn:";

($name,$password,$uid,$gid,$gcos,$home,$shell) = split(/:/,$line);

разбить $line, используя в качестве разделителя двоеточие

просто присваивает переменной

$shell нулевое значение (undef), если эта строка недостаточно длинна или содержит в последнем поле пустые значения. (Разбиение выполняется так, что лишние поля просто игнорируются.) join join берет список значений и "склеивает" их, ставя между элементами списка строку-связку. Выглядит это так:

$bigstring = join($glue,@list);

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

$outline = join(":", @fields) ;

Отметим, что строка-связка

- это не регулярное выражение, а обычная строка, состоящая из символов общим числом нуль или более.

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

$result = (join "+", "", @fields);

Здесь пустая строка

"" рассматривается как пустой элемент, который должен быть связан с первым элементом данных массива @fields.B результате связка помещается перед каждым элементом. Аналогичным образом можно поставить пустой элемент-связку в конец списка:

$output = join ("\n", @data, "");

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

Упражнения

Ответы к упражнениям даны в приложении А. Постройте регулярное выражение, которое соответствует:

а) минимум одному символу а, за которым следует любое число символов Ь;

б) любому числу обратных косых, за которым следует любое число звездочек (любое число может быть и нулем);

в) трем стоящим подряд копиям того, что содержится в переменной

г) любым пяти символам, включая символ новой строки;

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

а) Напишите программу, которая принимает список слов из stdin и ищет строку, содержащую все пять гласных (a,e,i,o ии). Запустите эту программу с /usr/dict/words* и посмотрите, что получится. Другими словами, введите программа

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

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

"i" и т.д. /etc/passwcf* (из stdin), выводя на экран регистрационное имя и реальное имя каждого пользователя. (Совет: с помощью функции split разбейте строку на поля, а затем с помощью sill избавьтесь от тех частей поля comment, которые стоят после первой запятой.) Напишите программу, которая просматривает файл /etc/passwd (из stdin) на предмет наличия двух пользователей с одинаковыми именами и выводит эти имена. (Совет: после извлечения первого имени создайте хеш с этим именем в качестве ключа и числом его экземпляров в качестве значения. Прочитав последнюю строку stdin, ищите в этом хеше счетчики с показанием больше единицы.) Повторите последнее упражнение, но с выдачей имен всех пользователей, зарегистрировавшихся под одинаковыми именами. (Совет: в хеше вместо числа экземпляров сохраните список регистрационных имен, записанных через пробелы. Затем ищите значения, содержащие пробел.) Словарь вашей системы может находиться не в каталоге /usr/dict/words; обратитесь к man-странице spell(l). Если используется NIS, то файл /etc/passwd в вашей системе будет содержать мало данных. Посмотрите, может быть, ypcat passwd даст больше информации.

Регулярные выражения представляют собой образцы для поиска заданных комбинаций символов в текстовых строках и замены их на другие комбинации символов (эти операции называются соответственно сопоставление с образцом и подстановка). Регулярное выражение имеет вид:

/pattern/modifiers

Здесь pattern — это строка, задающая регулярное выражение, а modifiers — необязательные однобуквенные модификаторы, уточняющие правила использования этого регулярного выражения.

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

Метасимволы в регулярных выражениях

Символ Описание
\ Для символов, которые обычно трактуются буквально, означает, что следующий символ является метасимволом. Например, /n/ соответствует букве n, а /\n/ соответствует символу перевода строки.
Для метасимволов означает, что символ должен пониматься буквально. Например, /^/ означает начало строки, а /\^/ соответствует просто символу ^. /\\/ соответствует обратной косой черте \.
^ Соответствует началу строки (ср. модификатор m ).
$ Соответствует концу строки (ср. модификатор m ).
. Соответствует любому символу, кроме разрыва строки (ср. модификатор s ).
* Соответствует повторению предыдущего символа нуль или более раз.
+ Соответствует повторению предыдущего символа один или более раз.
? Соответствует повторению предыдущего символа нуль или один раз.
(pattern ) Соответствует строке pattern и запоминает найденное соответствие.
x | y Соответствует x или y .
{ n } n — неотрицательное число. Соответствует ровно n вхождениям предыдущего символа.
{ n ,} n — неотрицательное число. Соответствует n или более вхождениям предыдущего символа. /x{1,}/ эквивалентно /x+/. /x{0,}/ эквивалентно /x*/.
{ n , m } n и m — неотрицательные числа. Соответствует не менее чем n и не более чем m вхождениям предыдущего символа. /x{0,1}/ эквивалентно /x?/.
[ xyz ] Соответствует любому символу из заключенных в квадратные скобки.
[^ xyz ] Соответствует любому символу, кроме заключенных в квадратные скобки.
[ a - z ] Соответствует любому символу в указанном диапазоне.
[^ a - z ] Соответствует любому символу, кроме лежащих в указанном диапазоне.
\a Соответствует символу звонок (BEL).
\A Соответствует только началу строки, даже с модификатором m .
\b Соответствует границе слова, т. е. позиции между \w и \W в любом порядке.
\B Соответствует любой позиции, кроме границы слова.
X Соответствует символу Ctrl+X. Например, /\cI/ эквивалентно /\t/.
\C Соответствует одному байту, даже при директиве use utf8 .
\d Соответствует цифре. Эквивалентно .
\D Соответствует нецифровому символу. Эквивалентно [^0-9].
\e Соответствует символу escape (ESC).
\E Конец преобразований \L , \Q , \U .
\f Соответствует символу перевода формата (FF).
\G Соответствует позиции в строке, равной pos() .
\l Преобразует следующий символ в нижний регистр.
\L Преобразует символы в нижний регистр до \E .
\n Соответствует разрыву строк.
\p property Соответствует символам Unicode, обладающим свойством property . Если property \p{ property } .
\P property Соответствует символам Unicode, не обладающим свойством property . Если property задается несколькими символами, используйте синтаксис \P{ property } .
\Q Добавляет символ "\\" перед метасимволами до \E .
\r Соответствует символу возврата каретки (CR).
\s Соответствует символу пробела. Эквивалентно /[ \f\n\r\t]/.
\S Соответствует любому непробельному символу. Эквивалентно /[^ \f\n\r\t]/.
\t Соответствует символу табуляции (HT).
\u Преобразует следующий символ в верхний регистр.
\U Преобразует символы в верхний регистр до \E .
\w Соответствует латинской букве, цифре или подчеркиванию. Эквивалентно / /.
\W Соответствует любому символу, кроме латинской буквы, цифры или подчеркивания. Эквивалентно /[^A-Za-z0-9_] /.
\X Соответствует последовательности символов Unicode из основного символа и набора диакритических значков. Эквивалентно выражению /C<(?:\PM\pM*)>/.
\z Соответствует только концу строки, даже с модификатором m .
\Z Соответствует только концу строки или разрыву строк в конце строки, даже с модификатором m .
\ n n — положительное число. Соответствует n -ной запомненной подстроке. Если левых скобок до этого символа меньше, чем n , и n > 9, то эквивалентно \0n .
\0 n n восьмеричное число, не большее 377. Соответствует символу с восьмеричным кодом n . Например, /\011/ эквивалентно /\t/.
\x n n шестнадцатеричное число, состоящее из двух цифр. Соответствует символу с шестнадцатеричным кодом n . Например, /\x31/ эквивалентно /1/.
\x{ n } n — шестнадцатеричное число, состоящее из четырех цифр. Соответствует символу Unicode с шестнадцатеричным кодом n . Например, /\x{2663}/ эквивалентно /♣/.

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

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

  • i - Игнорирует регистр символов при сопоставлении с образцом. При использовании директивы use locale приведение символов к одному регистру производится с учетом национальной настройки.
  • m - Рассматривает исходную строку как буфер из нескольких строк текста, разделенных разрывами строк. Это означает, что метасимволы ^ и $ соответствуют не только началу и концу всей строки, но и началу и концу строки текста, ограниченной разрывами строк.
  • s - Рассматривает исходную строку как единую строку текста, игнорируя разрывы строк. Это означает, что метасимвол. соответствует любому символу, включая разрыв строки.
  • x - Разрешает использование пробелов и комментариев. Пробелы, не имеющие предшествующего символа \ и не заключенные в , игнорируются. Символ # начинает комментарий, который также игнорируется.

Классы символов Unicode и POSIX

Мы можем использовать в регулярных выражениях синтаксис

[:class:]

где class задает название класса символов POSIX, т. е. мобильного стандарта на язык C. При использовании директивы use utf8 вместо классов POSIX можно использовать классы символов Unicode в конструкции

\p{class}

В следующей таблице сведены все классы символов POSIX, соответствующие классы символов Unicode и метасимволы, если они есть.

POSIX Unicode Метасимвол Описание
alpha IsAlpha Буквы
alnum IsAlnum Буквы и цифры
ascii IsAscii Символы ASCII
cntrl IsCntrl Управляющие символы
digit IsDigit \d Цифры
graph IsGraph Буквы, цифры и знаки пунктуации
lower IsLower Строчные буквы
print IsPrint Буквы, цифры, знаки пунктуации и пробел
punct IsPunct Знаки пунктуации
space IsSpace \s Символы пробела
upper IsUpper Прописные буквы
word IsWord \w Буквы, цифры и подчеркивание
xdigit IsXDigit Шестнадцатеричные цифры

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

/\d+/
/[:digit:]+/
/\p{IsDigit}+/ # use utf8

Для указания того, что символ не принадлежит к заданному классу, используются конструкции

[:^class:]
\P{class}

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

[:^digit:] \D \P{IsDigit}
[:^space:] \S \P{IsSpace}
[:^word:] \W \P{IsWord}

Запоминание подстрок

Использование круглых скобок в регулярном выражении приводит к тому, что подстрока, соответствующая образцу в скобках, запоминается в специальном буфере. Внутри функции, которая будет выполнять операцию со строкой при помощи вышеприведенного условия, совпадение будет запоминаться в специальных переменных, в PHP к ней можно обращаться через \1 в Perl - $1. В одном условии поиска может быть несколько инструкций запоминания: ({5})({4}) - проверит строку на совпадение с условием, в случае удачного совпадения, запомнит пять букв в \1 ($1), четыре цифры в \2 ($2). Если обратиться к переменной \0, то окажется, что в ней хранится вся совпавшая строка, которая была описана условием.

В РНР существует пять функций поиска по шаблону с использованием Perl-совместимых регулярных выражений:

  • preg_match()
  • preg_match_all()
  • preg_replace()
  • preg_split()
  • preg_grep()

Язык, созданный первоначально с главной целью облегчить обработку большого количества отчетов, просто обязан располагать развитыми средствами для работы с текстом. Напомним, что в среде UNIX, из которой вышел язык Perl, средства для обработки текстовых строк имеются в различных утилитах: sed, awk, grep, cut, а командный интерпретатор shell, также обладающий некоторыми средствами для обработки строк, позволяет организовать совместную работу этих утилит, передавая выход одной программы на вход другой через механизм, называемый конвейером. Такой подход требует написания достаточно изощренных сценариев на языке shell в сочетании с обращением к внутренним командам утилит обработки текста sed или awk. Язык Perl, являясь средством создания программ-сценариев, в то же время один обладает всеми возможностями перечисленных утилит и даже их превосходит. Типичная задача, возникающая при обработке текстового файла, заключается в том, чтобы найти в нем фрагмент, удовлетворяющий заданным условиям, и выполнить над найденным фрагментом некоторую операцию: удалить, заменить на другой фрагмент, извлечь для дальнейшего использования и т. д. Условия поиска можно достаточно просто выразить словами. Например: найти строку, содержащую слово Perl. Или: найти все фрагменты, находящиеся в конце строки и содержащие две цифры, за которыми следует произвольное количество прописных букв. Для формализованной записи подобных условий используются регулярные выражения, позволяющие описать образец, или шаблон поиска при помощи специальных правил. Манипуляции с регулярными выражениями осуществляются при помощи соответствующих операций, которые мы также рассмотрим в этой главе.

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

Регулярное выражение, по сути, представляет собой набор правил для описания текстовых строк. Сами правила записываются в виде последовательности обычных символов и метасимволов, которая затем в качестве образца используется в операциях поиска и замены текста. Метасимволы - это символы, имеющие в регулярном выражении специальное значение. Пользователи DOS/Windows хорошо знают метасимвол *, используемый для порождения имен файлов и обозначающий любую допустимую последовательность. Регулярные выражения используются многими программами UNIX, в том числе интерпретатором shell. Каждая из них использует свое множество метасимволов. В большинстве случаев метасимволы разных программ совпадают.

Метасимволы

В языке Perl к метасимволам относятся следующие символы: "\", ".", "^", "$", "|", "[", "]", "(", ")", "*", "+", "?", "{", "}" Различные метасимволы выполняют в регулярном выражении разные функции, в частности, используются для обозначения одиночного символа или группы символов, обозначают привязку к определенному месту строки, число возможных повторений отдельных элементов, возможность выбора из нескольких вариантов и т. д. Регулярное выражение, подобно арифметическому выражению, строится с соблюдением определенных правил. В нем можно выделить операнды (элементы) и операции. Простейшим регулярным выражением является регулярное выражение, состоящее из одного обычного символа. Обычный символ в регулярном выражении представляет самого себя. Соответственно, последовательность обычных символов представляет саму себя и не нуждается в дополнительной интерпретации. Для использования в операциях в качестве образца регулярное выражение заключается между двумя одинаковыми символами-ограничителями. Часто в качестве ограничителя используется символ косая черта (/). Например, образцу /Perl/ будут соответствовать все строки, содержащие слово Perl, Если в регулярном выражении какой-либо метасимвол требуется использовать в буквальном, а не специальном значении, его нужно экранировать, или маскировать, при помощи другого метасимвола - \. Например, образцу /\\\*/ соответствует фрагмент текста \*. Здесь первый метасимвол \ экранирует второй метасимвол \, а третий метасимвол \ экранирует метасимвол * Метасимвол. представляет любой одиночный символ, кроме символа новой строки. Так, образцу /./ будет соответствовать любая непустая строка. Если в операциях сопоставления с образцом установлен флаг s, то метасимволу. соответствует также и символ новой строки. Метасимвол [ используется в конструкции [...] для представления любого одиночного символа из числа заключенных в скобки, то есть он представляет класс символов. Два символа, соединенные знаком минус, задают диапазон значений, например задает все прописные и строчные буквы английского алфавита. Если первым символом в скобках является символ ^, вся конструкция обозначает любой символ, не входящий в число перечисленных в скобках. Например, [^0-9] обозначает все нецифровые символы. Ниже мы рассмотрим и другие способы представления классов символов. Метасимволы ^ и $ используются для задания привязки к определенному месту строки. Метасимвол ^ в качестве первого символа регулярного выражения обозначает начало строки. Метасимвол $ в качестве последнего символа регулярного выражения обозначает конец строки. Например, следующим образцам соответствуют: /^$/ - пустая строка (начало и конец, между которыми пусто); /^Perl/ - слово Perl в начале строки; /Perl$/ - слово Perl в конце строки. Метасимвол | можно рассматривать как символ операции, задающей выбор из нескольких вариантов (подобно логической операции ИЛИ). Например, образцу /а | b | с/ соответствует фрагмент текста, содержащий любой из символов а, b, с. Если вариантами выбора являются одиночные символы, как в данном примере, то лучше использовать конструкцию, определяющую класс символов, в данном случае . Но, в отличие от конструкции [...], операция | применима и тогда, когда вариантами выбора являются последовательности символов. Например, образцу /Word|Excel|Windows/ соответствует фрагмент текста, содержащий любое из слов Word, Excel, Windows. Следующая группа метасимволов служит в качестве коэффициентов, или множителей, определяющих количество возможных повторений отдельных атомарных элементов регулярного выражения. r* - нуль и более повторений r; r+ - одно и более повторений r; r? - нуль или одно повторение r; r{n} - ровно n повторений r; r{n,} - n и более повторений r; r{n,m} - минимум n, максимум m повторений r. Атомарные элементы, или атомы, - это простейшие элементы, из которых строится регулярное выражение. Это не обязательно одиночный символ. Вот несколько примеров использования множителей в регулярных выражениях: /.*/ - любая строка; /.+/ - любая непустая строка; /{3}/ - любая последовательность из трех цифр; /\[+/ - последовательность, состоящая из любого числа символов [. В первых двух примерах атомом является метасимвол. (точка). В третьем образце в качестве атома выступает конструкция , определяющая класс цифровых символов. В четвертом образце атом - это пара символов \[, включающая метасимвол \, отменяющий специальное значение следующего за ним метасимвола [. Полный список атомов мы приведем после изучения всех необходимых синтаксических конструкций. Алгоритм, применяемый в операциях поиска и замены для обработки регулярных выражений, содержащих множители, является «жадным»: он пытается найти для образца, снабженного множителем, максимальный сопоставимый фрагмент текста. Рассмотрим, например, что происходит при поиске в строке: «Скроен колпак не по-колпаковски, надо колпак переколпаковать» фрагмента, удовлетворяющего образцу /.*колпак/. Алгоритм найдет максимальный фрагмент, удовлетворяющий выражению.* (вся строка без завершающего символа новой строки), затем начнет двигаться назад, отбрасывая в найденном фрагменте по одному символу, до тех пор, пока не будет достигнуто соответствие с образцом. Найденный фрагмент будет иметь вид: «Скроен колпак не по-колпаковски, надо колпак переколпак». Можно заставить алгоритм работать иначе, снабдив множитель * модификатором?. В этом случае алгоритм из «жадного» превращается в «ленивый» и будет для образца, снабженного множителем, искать минимальный соответствующий фрагмент. «Ленивый» алгоритм для множителя *? начнет поиск в строке с пустого фрагмента "", добавляя к нему по одному символу из строки до тех пор, пока не достигнет соответствия с образцом. В этом случае найденный фрагмент будет иметь вид: «Скроен колпак». Все сказанное справедливо и для других множителей. Например, в строке "1234567" будет найден: для образца /\d*/ - максимальный фрагмент "1234567"; для образца /\d+/ - максимальный фрагмент "1234567"; для образца/\d?/ - максимальный фрагмент "1"; для образца /\d{2,5}/ - максимальный фрагмент "12345"; для образца /\d*?/ - минимальный фрагмент ""; для образца /\d+?/ - минимальный фрагмент "1"; для образца /\d??/ - минимальный фрагмент ""; для образца /\d{2,5}?/ - минимальный фрагмент "12".

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

Символ \, непосредственно предшествующий одному из метасимволов, отменяет специальное значение последнего. Если же символ \ непосредственно предшествует обычному символу, то, напротив, такая последовательность символов во многих случаях приобретает специальное значение. Подобного рода последовательности будем называть метапоследовательностями, Метапоследовательности в регулярном выражении служат, в основном, для представления отдельных символов, классов символов или определенного места в строке, дополняя и иногда дублируя функции метасимволов. Рассмотрим существующие метапоследовательности.
  • \nnn - представляет символ, восьмеричный код которого равен nnn. Например, последовательность \120\145\162\154 представляет слово Perl (\120 - восьмеричный код буквы Р, \145 - буквы е, \162 - буквы r, \154 - буквы l).
  • \xnn - представляет символ, шестнадцатеричный код которого равен nn. Слово Perl, например, представляется последовательностью \x50\x65\x72\x6C.
  • \cn - представляет управляющий символ, который генерируется при нажатии комбинации клавиш Ctrl+n, где n- символ, например \cD соответствует Ctrl+D.
  • \$- символ $.
  • \@ - символ @.
  • \% - символ %
  • \а - представляет символ с десятичным ASCII-кодом 7 (звонок). При выводе производит звуковой сигнал.
  • \е - символ Esc, десятичный ASCII-код 27.
  • \f - символ перевода страницы, десятичный ASCII-код 12.
  • \n - символ новой строки, десятичный ASCII-код 10.
  • \r - символ «возврат каретки», десятичный ASCII-код 13.
  • \t - символ горизонтальной табуляции, десятичный ASCII-код 9.
  • \v - символ вертикальной табуляции, десятичный ASCII-код 11.
  • \s - представляет класс пробельных символов. К пробельным символам относятся пробел, символ табуляции, возврат каретки, символ новой строки и символ перевода страницы. То же самое, что и[ \t,\r,\n,\f].
  • \S - представляет класс непробельных символов, то же самое, что и класс [^ \t, \r,\n,\f].
  • \d - класс цифровых символов, тоже, что и .
  • \D - класс нецифровых символов, то же, что и [^0-9].
  • \w - представляет класс буквенно-цифровых символов, состоящий из букв, цифр и символа подчеркивания _. То же самое, что и . Обратите внимание, что в этот класс входят только буквы английского алфавита.
  • \W - представляет класс небуквенно-цифровых символов. То же самое, что и выражение [^a-zA-Z_0-9].
  • \А - обозначает начало строки.
  • \Z - обозначает конец строки.

ПРИМЕЧАНИЕ Последовательность \А эквивалентна метасимволу ^ в начале регулярного выражения, а последовательность \Z - метасимволу $ в конце регулярного выражения, за исключением одного случая. Назовем строку, содержащую внутри себя символы новой строки (ASCII 10), мультистрокой. Фактически мультистрока состоит из отдельных строк, разделенных ограничителями - символами новой строки. При выводе мультистрока отображается в виде нескольких строк. Если к мультистроке применяется операция поиска или замены с опцией /m, то последовательности \А и \Z обозначают соответственно начало и конец всей мультистроки, а метасимволам ^ и $ соответствуют еще и границы внутренних строк, образующих мультистроку.

  • \b - обозначает границы слова. Под словом понимается последовательность символов из класса \w. Граница слова определяется как точка между символами из класса \w и символами из класса \W.
  • \В - обозначает не-границы слова, то есть класс символов [^\b].
  • \l - означает, что следующий символ регулярного выражения преобразуется в нижний регистр. Например, запись /\lP/ означает, что символ Р будет преобразован в нижний регистр, после чего новый образец /p/ может быть использован в соответствующей операции поиска или замены,
  • \u - означает, что следующий символ регулярного выражения преобразуется в верхний регистр.
  • \L...\Е - означает, что все символы в регулярном выражении между \L и \Е преобразуются в нижний регистр.
  • \U...\Е -означает, что все символы в регулярном выражении между \U и \Е преобразуются в верхний регистр.
  • \Q...\Е - означает, что все метасимволы в регулярном выражении между \Q и \Е экранируются при помощи символа \. Например, запись /\Q^*?+\Е/ эквивалентна записи /\^\*\?\+/.
  • \G - обозначает точку, в которой закончился предыдущий поиск m//g (см. описание операции поиска m//).

Атомы

Из всех метасимволов, перечисленных в начале параграфа, нам осталось рассмотреть метасимволы (и). Они служат для группирования ряда элементов, входящих в состав образца, в один элемент. Например, образцу /(abc)+/ соответствует строка, состоящая из одного или более повторений последовательности abc, в то время как образцу /abc+/ - строка, состоящая из начальных символов ab, за которыми следует один или более символов с. Теперь мы можем перечислить атомы, из которых строится регулярное выражение.
  • Регулярное выражение в скобках, представляющее несколько элементов, сгруппированных в один элемент.
  • Любой обычный символ (не метасимвол).
  • Символ. , представляющий любой одиночный символ, кроме символа новой строки.
  • Конструкция [...], представляющая класс символов, перечисленных в квадратных скобках.
  • Метапоследовательность, представляющая символ или класс символов: \а, \n, \r, \t, \f, \e, \d, \D, \w, \W, \s, \S.
  • Метапоследовательность вида \nnn, определяющая символ при помощи его восьмеричного ASCII-кода nnn.
  • Метапоследовательность вида \xnn, определяющая символ при помощи его шестнадцатеричного ASCII-кода nn.
  • Метапоследовательность вида \cn, представляющая управляющий символ Ctrl+n.
  • Конструкция вида \number, представляющая обратную ссылку (см. следующий раздел).
  • Любая конструкция вида \character, не имеющая специального значения, а представляющая собственно символ character, например: \*, \у, \h. Напомним, что в регулярном выражении множители *, +, ?, {n,m} применяются именно к атому, расположенному непосредственно слева.

    Обратные ссылки

    Ранее мы установили, что группу элементов регулярного выражения можно заключить в скобки и рассматривать как один элемент. Заключение группы элементов в скобки имеет дополнительный и весьма полезный эффект. Если в результате поиска будет найден фрагмент текста, соответствующий образцу, заключенному в скобки, то этот фрагмент будет сохранен в специальной переменной, и внутри регулярного выражения к нему можно будет обратиться, используя запись \number, где number- номер конструкции (...) в исходном регулярном выражении. Запись \number, указывающую на найденный по образцу фрагмент текста, будем называть обратной ссылкой. Можно задать любое количество конструкций вида (...) и ссылаться на соответствующие найденные фрагменты текста как на \1, \2 и т. д. Например, образцу /(.+)-\1/ соответствуют слова «ха-ха», «хи-хи», «ку-ку!> и т. п., а образцу /(.)(.).?\2\1/ соответствуют все палиндромы из четырех или пяти букв, (Палиндром - слово или предложение, которое одинаково читается слева направо и справа налево.) Внутри образца конструкция \n (n= 1,...,9) всегда обозначает обратную ссылку. Запись вида \nn также интерпретируется как обратная ссылка, но только в том случае, если в исходном выражении задано не менее, чем nn скобочных конструкций вида (...). Иначе запись \nn обозначает символ с восьмеричным кодом nn. Для ссылки на найденный фрагмент текста за пределами регулярного выражения, например, при задании замещающего текста в операции замены, вместо записи \number используется запись $number. Например, операция замены $str =~ s/(\S+)\s+(\S+)/$2 $1/ меняет местами первые два слова в строке $str. Область действия переменных $1, $2 и т. д. распространяется до наступления одного из следующих событий: конец текущего блока; конец строки, являющейся аргументом функции eval; следующее совпадение с образцом. Аналогичным образом определяется область действия и для следующих предопределенных переменных, используемых в операциях сопоставления с образцом:
    • $& - часть строки, найденная при последней операции сопоставления с образцом;
    • $` - часть строки, стоящая перед совпавшей частью при последней успешной операции сопоставления с образцом;
    • $" - часть строки, стоящая после совпавшей части при последней успешной операции сопоставления с образцом.
    Например, в результате выполнения операции поиска $str =~ m/two/ в строке $str = "one two three" образца /two/ будут присвоены следующие значения переменным:
    • $& - "two";
    • $` - "one";
    • $" - "three".
    Эти значения будут сохраняться до наступления одного из перечисленных выше событий, и их можно использовать, например, для формирования строки с обратным порядком следования слов: $rstr=$".$&.$`. Строка $rstr будет иметь вид: "three two one". Следует отметить, что, если обращение к одной из переменных $&, $`, $" встречается где-либо в программе, то интерпретатор perl будет вычислять и запоминать их для каждой операции сопоставления с образцом, что, в свою очередь, замедляет выполнение всей программы. Поэтому не следует использовать данные переменные без особой необходимости.

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

    Выше мы использовали скобки для группирования нескольких элементов регулярного выражения в один элемент. Побочным эффектом данной операции является запоминание найденного фрагмента текста, соответствующего образцу, заключенному в скобки, в специальной переменной. Если скобки используются только для группирования элементов регулярного выражения, то найденный фрагмент текста можно не запоминать. Для этого после открывающей скобки (следует поместить конструкцию?:, например в случае задания альтернативы - /(?:Реrl|реrl)/. Конструкция (?:pattern) относится к классу конструкций общего вида (?...), добавляющих новые возможности для задания образцов за счет расширения синтаксиса регулярного выражения, а не за счет введения новых метасимволов или метапоследовательностей. Символ, следующий за символом?, определяет функцию, выполняемую данной синтаксической конструкцией. В настоящее время определены около десяти расширенных конструкций регулярного выражения, большая часть которых рассмотрена в данном разделе. Оставшиеся конструкции, на наш взгляд, не являются необходимыми для первоначального знакомства с языком. (?#text) - комментарий. Текст после символа # и до закрывающей скобки) игнорируется интерпретатором и используется для добавления комментария непосредственно в регулярное выражение. (?imsx-imsx: pattern) - использовать скобки только для группирования элементов без создания обратных ссылок. Символы imsx-imsx между вопросительным знаком и двоеточием интерпретируются как флаги, модифицирующие функцию данного выражения (см. ниже). (?=pattern) - следующий фрагмент в тексте должен соответствовать образцу pattern. Обычно образец для операций поиска или замены задается при помощи регулярного выражения. Результатом операции поиска является фрагмент, соответствующий образцу, который сохраняется в специальной переменной $&. Конструкция (?=pattern) в составе регулярного выражения позволяет задать условие поиска, не включая найденный фрагмент, соответствующий образцу pattern, в результат, сохраняемый в переменной $&. Конструкция (?=pattern) в регулярном выражении задает условие, что следующий фрагмент текста должен удовлетворять образцу pattern. Обращаем внимание на слово следующий. Данная конструкция неприменима для задания условия, что предыдущий фрагмент текста должен соответствовать заданному образцу. Например, образцу/b+(?=с+)/соответствует часть строки, состоящая из одной или более литер b, за которыми следуют одна или более литер с, причем найденный фрагмент текста будет содержать только последовательность литер b без последовательности литер с. Рассмотрим, например, строку: $str = "aaabbbcccddd"; В результате операции поиска $str =~ m/b+(?=c+)/; будут сохранены следующие значения в специальных переменных: S` - ааа, $& - bbb, $" - cccddd. Если в операции поиска указать образец /b+с+/, то значения специальных переменных будут следующими: S` - ааа, $&- bbbccc, $" - ddd. B свою очередь, операция поиска по образцу /(?=b+)с+/ в нашем примере не даст результата. Данный образец задает условие, что следующий фрагмент текста должен содержать непустую последовательность литер b. В нашей строке такой фрагмент будет найден, это фрагмент bbb, но он не будет включен в результат поиска. Следующий фрагмент, в соответствии с образцом, должен представлять непустую последовательность литер с, но в нашем случае этого соответствия не будет, так как мы остановились перед фрагментом bbb, не включив его в результат, и следующим фрагментом поэтому будет bbb, а не ссс. Конструкцию (?=pattern) будем называть регулярным выражением с положительным постусловием. (?!pattern) - конструкция в регулярном выражении задает условие, что следующий фрагмент текста не должен удовлетворять образцу pattern. Найденный фрагмент не запоминается в переменной $&. Например, результат операции поиска $str =~ m/b+(?!с+)/; в рассмотренной выше строке $str будет зафиксирован в следующих значениях специальных переменных: S` - ааа, S& - bb, $" - bcccddd. Найденная подстрока соответствует образцу: она состоит из двух литер bb, за которыми не следует последовательность литер с. По аналогии с предыдущей конструкцией данную конструкцию назовем регулярным выражением с отрицательным постусловием. (?$str =~ m/(?<=b)b+/; значения специальных переменных будут распределены следующим образом: S` - ааа, $& - bb, $" - cccddd. Данную конструкцию назовем регулярным выражением с положительным предусловием. (?
  • i - поиск без учета регистра;
  • m - строка трактуется как мультистрока, состоящая из нескольких строк, разделенных символом новой строки;
  • s - строка трактуется как одна строка, в этом случае метасимволу. соответствует любой одиночный символ, включая символ новой строки;
  • x - разрешается использовать в образцах пробелы и комментарии. При использовании флага х пробелы в образцах игнорируются. Признаком комментария является символ #, как и в основном тексте Perl-программы. Пробелы позволяют сделать образец лучше читаемым.
Одна из литер i , m, s, x после знака - обозначает отмену соответствующего флага. При помощи данной расширенной конструкции можно задать, например, следующий образец: /(?ix) perl # игнорирование регистра при поиске/ Флаг i предписывает не учитывать регистр в операциях сопоставления с образцом, так что образцу будет соответствовать и слово «perl», и слово «Perl». Флаг х позволяет выделить слово «perl» пробелами и использовать непосредственно в образце комментарий. И пробелы, и комментарий не будут учитываться в операции сопоставления с образцом.

Сводка результатов

Изложенное в данном параграфе можно суммировать в виде набора правил, которыми следует руководствоваться при работе с регулярными выражениями.
  1. Любой одиночный символ, не являющийся метасимволом, представляет самого себя.
  2. Специальное значение метасимвола можно отменить, поместив перед ним специальный экранирующий метасимвол \.
  3. Можно определить класс символов, заключив их в квадратные скобки. Если первым после открывающей скобки [ является символ ^, то вся конструкция обозначает класс символов, не входящих в число перечисленных в скобках. Внутри скобок два символа, соединенные знаком -, определяют диапазон. Чтобы включить в состав класса символ -, его следует поместить в начале или в конце списка либо экранировать при помощи символа \.
  4. Символы можно задавать при помощи метапоследовательностей, состоящих из символа \, за которым следует обычный символ или последовательность символов.
  5. Альтернативный выбор задается перечислением вариантов, разделенных символом |. Обычно вся конструкция при этом заключается в круглые скобки.
  6. Внутри регулярного выражения можно выделить подобразец, заключив его в круглые скобки. На n-ю конструкцию в скобках можно затем сослаться, используя нотацию \n внутри и $n - вне регулярного выражения.
В заключение приведем сводку метасимволов и метапоследовательностей, рассмотренных в данной главе. Таблица 8.1. Символы, имеющие специальное значение в регулярном выражении Perl
Метасимвол Интерпретация
\ Отменяет (экранирует) специальное значение следующего за ним метасимвола
. Любой одиночный символ, кроме символа новой строки. Любой одиночный символ, включая символ новой строки, если в операции сопоставления с образцом задан флаг s
^ Обозначает начало строки, если является первым символом образца
$ Обозначает коней строки, если является последним символом образца
| Разделяет альтернативные варианты
[...] Любой одиночный символ из числа перечисленных в квадратных скобках. Пара символов, разделенных знаком минус, задает диапазон символов. Например, задает все прописные и строчные буквы английского алфавита. Если первым символом в скобках является символ ^, вся конструкция обозначает любой символ, не входящий в число перечисленных в скобках. Внутри скобок символы. * [ и \ теряют свое специальное значение.
(...) Группирование элементов образца в один элемент
* Нуль и более повторений регулярного выражения, стоящего непосредственно перед *
+ Одно или более повторений регулярного выражения, стоящего Непосредственно перед +
? Одно или ни одного повторения регулярного выражения, стоящего непосредственно перед?
{n, m} Минимальное n и максимальное m число повторений регулярного выражения, стоящего перед {n, m}. Конструкция {n} означает ровно n повторений, {n,} - минимум n повторений

Таблица 8.2. Метапоследовательности в регулярных выражениях Perl
Метапоследовательность Значение
\0nn Символ, восьмеричный код которого равен nn
При выводе производит звуковой сигнал
Обозначает начало строки
\b Обозначает границы слова. Под словом понимается последовательность символов из класса \w. Граница слова определяется как точка между символами из класса \w и символами из класса \W
\B Обозначает не-границы слова
\cn Управляющий символ, который генерируется при нажатии комбинации клавиш Ctrl+n
\d Любой цифровой символ, то же, что и
\D Любой нецифровой символ, то же, что и [^0-9]
Символ Esc, ASCII 27
\E Ограничитель последовательностей \L, \U, \Q
\f Символ перевода страницы, ASCII 12
\G Обозначает точку, в которой закончился предыдущий поиск m//g
\l Преобразует следующий символ регулярного выражения в нижний регистр
\L Преобразует все последующие символы в регулярном выражении в нижний регистр до тех пор, пока не встретится последовательность \Е
\n Символ новой строки, ASCII 10
\Q Эквивалентно экранированию всех последующих метасимволов в регулярном выражении при помощи символа \ до тех пор, пока не встретится последовательность \Е
\r Символ «возврат каретки», ASCII 13
\s Класс пробельных символов: пробел (space), символ табуляции (tab), возврат каретки (carriage return), символ перевода строки (line feed) и символ перевода страницы (form feed); эквивалентно [\t,\r,\n,\f]
\S Класс непробельных символов
\t Символ табуляции, ASCII 9
\u Преобразует следующий символ в верхний регистр
\U Преобразует все последующие символы в регулярном выражении в верхний регистр до тех пор, пока не встретится последовательность \Е
\v Символ вертикальной табуляции, ASCII 11
\w Любая буква, цифра или символ подчеркивания
\W Любой символ, не являющийся буквой, цифрой или символом подчеркивания
\xnn Символ, шестнадцатеричный код которого равен nn
\Z Обозначает конец строки

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

Как правило все эти операторы имеют схожие опции такие как:

i - не различать строчные и заглавные буквы
m - считать строку многострочной
s - однострочная строка
x - расширенный синтаксис (использование пробелов и комментариев)

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

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

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

\ - считать следующий метасимвол как обычный символ
^ - начало строки
. - один произвольный символ. Кроме "\n" - конец строки
$ - конец строки
| - альтернатива (или)
() - группировка
- класс символов

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

* - повторяется 0 или большее число раз
+ - повторяется 1 или большее число раз
? - 1 или 0 раз
{n} - точно n раз
{n,} - по меньшей мере раз
{n,m} - не менше n, но и не больше m

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

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

*? - станет 0 и более
+? - 1 и более
?? - 0 или 1 раз
{n}? - точно n раз
{n,}? - не меньше n раз
{n,m}? - больше или равно n и меньше m раз

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

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

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

\w - алфавитно-цифровой или "_" символ
\W - не алфавитно-цифровой или "_" символ
\s - один пробел
\S - один не пробел
\d - одна цифра
\D - одна не цифра

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

\w+ - слово
\d+ - целое число
[+-]?\d+ - целое со знаком
[+-]?\d+\.?\d* - число с точкой

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

\b - граница слова
\B - не граница слова
\A - начало строки
\Z - конец строки
\G - конец действия m//g

Граница слова (\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" }

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

(?#комментарий) - комментарий в теле шаблона.
(?:шаблон) - группировка как и "()" но без обратной ссылки
(?=шаблон) - "заглядывание" вперед.

Например /\w+(?=\t)/ соответствует слову, за которым идет табуляция, но символ "\t" не включается в результат.

$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".