Регулярные выражения Perl и их применение

Операторы m// и s///

Разбить на страницы
Показывать лекцию целиком

Оператор m// в общем виде записывается так:

$text =~ m/ регулярное выражение / модификаторы

или

$text !~ m/ регулярное выражение / модификаторы

Второй случай эквивалентен записи

not $text =~ m/ регулярное выражение / модификаторы

при которой возвращаемое значение логически инвертируется.

Целевым текстом по умолчанию является переменная $_. В этом случае ее и связку можно не писать.

$_ =~ m/ регулярное выражение / модификаторы

эквивалентно

m/ регулярное выражение / модификаторы

Если ограничителями являются слэши, то букву m также можно опустить.

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

//

или задано в виде пустой или неопределенной переменной:

/$emtyvar/

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

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

my $re=qr/ регулярное выражение /;
$text =~ $re;

или

$text =~ /$re/;

Оператор подстановки

$text =~ s/ регулярное выражение / выражение для подстановки / модификаторы

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

4.1. Модификаторы операторов m// и s///

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

  • i - игнорирует различие между заглавными и строчными буквами. На этот модификатор влияет установка локали.
  • s - метасимвол "точка" совпадает со всеми символами, включая символ новой строки \n.
  • m - разрешает привязку метасимволам ^ и $ к промежуточным символам \n в тексте. В этом случае метасимвол ^ совпадает не только в начале текста, как якорь \A, но и после каждого символа \n, который не стоит в самом конце текста. А метасимвол $ начинает совпадать не только в самом конце текста и перед \n, стоящим в самом конце текста, как якорь \Z, но также после каждого символа \n, который не стоит в самом конце текста.
  • x - игнорирует пробельные символы в регулярном выражении, разрешает использовать внутри шаблона комментарии.
  • g - поиск и замена выполняются глобально (в неявном цикле). Подробности мы рассмотрим.
  • c - работает только с модификатором g и только с оператором m//, который применяется в скалярном контексте. Запрещает сбрасывать текущую позицию поиска, когда не удалось найти очередного совпадения. Подробности будут далее.
  • o - шаблон с этим модификатором однократной компиляции транслируется один раз во время компиляции всей программы. Здесь дело в том, что внутри регулярного выражения могут встречаться переменные, которые будут интерполироваться, т.е. их значения будут подставляться в шаблон в качестве литералов. (Не путайте эти переменные с теми, которые присутствуют в исполняемом коде Perl, такие переменные не интерполируются.) Если интерпретатор видит, что происходит интерполяция переменных, то он каждый раз перед использованием этого оператора поиска/замены будет транслировать регулярное выражение во внутреннее представление (байт код). Это может отнять много времени, если такое выражение используется в цикле. Но если вы уверены, что интерполируемая переменная (или массив) не меняет своего значения, то можете поставить к такому регулярному выражению модификатор o, чтобы избежать его многократной компиляции. Этот модификатор может стоять только после всего регулярного выражения.
  • 4.2. Алгоритм работы операторов m// и s///

    В языке Perl есть такое понятие - контекст выражения. Операторы могут возвращать значение, которое зависит от того, чему оно присваивается. Если оно присваивается скалярной переменной, то возвращаемое значение может быть одно, а если присвоение происходит массиву, то возвращаться может уже список значений. Таким является оператор m//. Например, если мы используем его в условии условного оператора:

    if (m/…/) { … }

    то это скалярный контекст, т.к. в условии ожидается одно значение, которое трактуется как истина или ложь. А если написать

    my @m=m/…/;

    то это списковый контекст, в котором оператор m// возвратит список значений. Оператор print является списковым, т.к. он ожидает список в качестве своего аргумента, поэтому, если написать

    print m/…/;

    то это также будет использование оператора поиска в списковом контексте.

    4.2.1. Оператор m// в режиме однократного поиска и в скалярном контексте

    В режиме однократного поиска (т.е. без модификатора g ) и в скалярном контексте оператор m// возвращает логическое значение: целое число 1, если поиск оказался успешным, или пустую строку в случае неудачи. Если в шаблоне имелись захватывающие скобки, то при успешном поиске создаются нумерованные переменные $1, $2, …, которые содержат соответствующие фрагменты захваченного текста. Если поиск оказался неудачным, то эти переменные хранят последнее состояние от предыдущего оператора поиска или замены.

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

  • $ - копия текста, совпавшего со всем регулярным выражением. Если вы заключите все регулярное выражение в круглые скобки: m/(…)/, то в случае успеха в переменной $1 получите то же самое значение.
  • $` - копия текста, предшествующего началу совпадения (т.е. текст, который был слева от совпадения).
  • $' - копия текста, следующего после совпадения (т.е. расположенного справа от совпавшего текста). После успешного совпадения текст "$`$$'" представляет из себя копию исходного текста. Есть малозначительное исключение: если оператор m// был успешно применен к неопределенной переменной, то эти специальные переменные будут иметь пустые значения.
  • $+ - копия переменной $1 или $2… с максимальным номером, которой было присвоено значение. Если в регулярном выражении нет сохраняющих скобок или они не задействованы в совпадении, то эта переменная получает неопределенное значение.
  • $^N - копия переменной $1 или $2…, которая соответствует последней только что закрытой паре скобок на момент использования этой переменной. Если в регулярном выражении нет сохраняющих скобок или они не задействованы в совпадении, то эта переменная получает неопределенное значение.
  • @ - массив начальных смещений в целевом тексте. $-[0] хранит смещение начала совпадения от начала текста для всего регулярного выражения. $-[1] хранит смещение начала совпадения от начала текста для фрагмента текста, захваченного в переменную $1, $-[2] - для переменной $2 и т.д.
  • @+ - аналогичный массив конечных смещений в целевом тексте. Т.е. смещений символов, следующих за конечными символами совпадения во всем тексте, в переменной $1, $2 и т.д. Имеет место соотношение substr($text,$-[0],$+[0]-$-[0]) эквивалентно $, substr($text,$-[1],$+[1]-$-[1]) эквивалентно $1 и т.д.
  • Теперь буду давать пояснения к этому списку. Во-первых, имейте в виду, что переменная $0, похожая на нумерованную, не имеет отношения к регулярным выражениям, а хранит имя сценария Perl, запущенного на выполнение.

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

    $text =~ /(…)|(…)|(…)/;

    Мы знаем, что совпадение может быть не более чем с одной альтернативой, и хотим получить фрагмент текста, совпавшего с какой-либо альтернативой. Не будь переменной $+, нам пришлось бы перебирать переменные $1, $2,… и определять, какая из них имеет определенное значение.

    Для понимания отличия переменной $+ от $^N рассмотрим такой пример:

    print "\$+=$+, \$^N=$^N" if "abc" =~ /(a(bc))/;

    На печати окажется строка

    $+=bc, $^N=abc

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

    Использовать значения переменных $+ и $^N можно также в коде Perl внутри регулярного выражения. Эти переменные корректируются после того, как закроется очередная захватывающая скобка. Например:

    print "\$+=$+, \$^N=$^N"
     if "abcd" =~ /(a(bc)(d)(?{ print "\$+=$+, \$^N=$^N\n" }))/;

    Здесь выводятся значения переменных $+ и $^N после третьей скобки внутри регулярного выражения, а также после того, как оператор поиска отработал. В итоге получается результат:

    $+=d, $^N=d
    $+=d, $^N=abcd

    $+ в обоих случаях относится к нумерованной переменной с максимальным номером - $3, а переменная $^N внутри регулярного выражения копирует переменную $3, а вне регулярного выражения является копией переменной $1.

    4.2.2. Оператор m// в списковом контексте без модификатора g

    Рассмотрим работу оператора m// в списковом контексте в режиме однократного поиска (т.е без модификатора g ). Возвращаемое значение зависит от того, есть ли в шаблоне захватывающие скобки. Если есть хотя бы одна пара захватывающих скобок, то в результате будет возвращен список из значений всех нумерованных переменных по числу использованных в регулярном выражении захватывающих скобок: ($1, $2, $3, …).

    Сами эти нумерованные переменные также создаются. В этом варианте оператор m// обычно применяется, когда надо извлечь из строки заранее известное число групп переменная=значение. Например:

    my $url='var1=value1var2=value2';
    my @pairs=$url =~ /(\w+)=(\w+)(\w+)=(\w+)/;
    print join ',',@pairs;

    На печать выйдет строка

    var1,value1,var2,value2

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

    В случае неудачного поиска в обоих случаях возвращается пустой список.

    Вот пример идиомы для применения оператора m// в списковом контексте:

    my $date='2007/3/12';
    if (my($year,$month,$day)=$date =~ m!^(\d+)/(\d+)/(\d+)$!)
     { print "Year=$1, month=$2, day=$3";
     } else
      { print 'Not found!';
      }

    На печать выведется

    Year=2007, month=3, day=12

    Мы присваиваем вовращаемое значение списку, поэтому применяется списковый контекст оператора поиска. Затем в операторе if этот список рассматривается в скалярном контексте, что дает число элементов этого списка. Полученный результат используется для проверки успешности выполнения оператора поиска.

    4.2.3. Оператор m// в скалярном контексте с модификатором g

    Оператор поиска в скалярном контексте и с модификатором g ведет себя особым образом: переменная, к которой применяется поиск, будет неявно хранить позицию, на которой остановился последний поиск оператором m// в этой переменной. При очередном применении к этой переменной оператора поиска с модификатором g (в списковом или скалярном контексте) поиск продолжится не с начала текста, а с той позиции, на которой он остановился в последний раз. Это позволяет применить к одной и той же переменной несколько операторов поиска и извлекать из текста элементы один за другим. Эта позиция, на которой остановился оператор m//g в последний раз, может быть получена при обращении к функции pos с тем же аргументом, к которому применялся этот поиск.

    Вот пример:

    $_='1234567890';
    /(\d)(\d)/g;
    print "$1,$2\n";
    my @a=/(\d)(\d)/g;
    print join ',',@a;

    На печать выйдут строки

    1,2
    3,4,5,6,7,8,9,0

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

    В случае применения оператора поиска к одной и той же константе:

    while ('abcd'=~/(\w)/g) { print $1 }

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

    abcd

    4.2.4. Оператор m// в списковом контексте с модификатором g

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

    Пример:

    my $text='123 234 345 456';
    my @m=$text =~ /(\d+)(\s+)/g;
    print @m;

    Будет напечатана строка

    123 234 345 456

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

    my $text='123 234 345 456';
    my @m=$text =~ /\d+/g;
    print join ',',@m;

    Будет напечатано

    123,234,345,456

    4.2.5. Работа оператор s/// с модификатором g и без него

    Оператор замены s/// без модификатора g ищет в заданной переменной прототип регулярного выражения и в случае нахождения заменяет его на выражение для замены, после чего заканчивает свою работу. При наличии модификатора g замена производится столько раз, сколько раз был найден фрагмент текста, соответствующий регулярному выражению. В результате все найденные фрагменты будут заменены на заданное выражение. В результате возвращается число произведенных замен или пустая строка (или соответствующий список в случае спискового контекста). Например:

    my $text='123 234 345 456';
    $text =~ s/\d+/a/;
    print $text;

    Получим строку

    a 234 345 456

    Еще пример:

    my $text='123 234 345 456';
    $text =~ s/\d+/reverse $/ge;
    print $text;

    Здесь получим такой результат:

    321 432 543 654

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

    $text =~ s/(\d+)/reverse $1/ge;

    В операторах m// и s/// специальные переменные $1, $2, …, $, $`, $' и т.д. для каждой итерации, обусловленной модификатором g, создаются заново (локализуются). В результате при замене используются нужные значения этих переменных, которые возникли при последней итерации поиска.

    4.3. Предварительная обработка регулярных выражений

    Регулярные выражения в операторах m// и s/// обрабатываются особым образом. Вообще говоря, они "обрабатываются как строка в кавычках с учетом специфики регулярных выражений". Но если в операторе поиска/подстановки регулярное выражение ограничено апострофами, то такое регулярное выражение, как обычно пишут, "обрабатывается как строка в апострофах", т.е. интерполяция переменных в нем не производится. Здесь надо уточнить, что такие эскейп-последовательности, как \r, \n, \t, все равно работают в регулярном выражении, которое ограничено апострофами, хотя в строке, ограниченной апострофами, они не работают. В этом - специфика регулярных выражений.

    В части замены оператора s/// действует то же правило, за тем исключением, когда там стоит код Perl (есть модификатор e ). В строке, ограниченной апострофами, имеются только два метасимвола: апостроф и обратный слэш. Здесь замечу, что в строке замены, которая ограничена апострофами, эскейп-последовательности \r, \n, \t, … не работают. В случае, если регулярное выражение не ограничено апострофами, в нем символы $ и @ являются метасимволами. Но символ $ используется как якорь для конца строки, не получается ли здесь конфликта? Этот вопрос мы рассмотрим позднее.

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

    Замечу еще, что внутри классов переменные тоже интерполируются. Для закрепления этого материала рассмотрим такие примеры:

    $_='abc';
    my $s='c';
    print '1' if /^ab$s$/;

    Будет напечатана единица. После интерполяции регулярное выражение станет таким:

    /^abс$/

    Другой пример:

    $_='abc';
    my $s='ab';
    print /[$s]/g;

    Напечатается ab. Это регулярное выражение эквивалентно такому:

    /[ab]/g

    Класс совпал два раза и захватил на первой итерации букву a, а на второй - букву b.

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

    После интерполяции переменных происходит обработка конструкций изменения регистра символов /L, /l, /U, /u, а также конструкций вставки литерального текста \Q…\E.

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

    Страницы:

    Оператор m// в общем виде записывается так:

    $text =~ m/ регулярное выражение / модификаторы

    или

    $text !~ m/ регулярное выражение / модификаторы

    Второй случай эквивалентен записи

    not $text =~ m/ регулярное выражение / модификаторы

    при которой возвращаемое значение логически инвертируется.

    Целевым текстом по умолчанию является переменная $_. В этом случае ее и связку можно не писать.

    $_ =~ m/ регулярное выражение / модификаторы

    эквивалентно

    m/ регулярное выражение / модификаторы

    Если ограничителями являются слэши, то букву m также можно опустить.

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

    //

    или задано в виде пустой или неопределенной переменной:

    /$emtyvar/

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

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

    my $re=qr/ регулярное выражение /;
    $text =~ $re;

    или

    $text =~ /$re/;

    Оператор подстановки

    $text =~ s/ регулярное выражение / выражение для подстановки / модификаторы

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

    4.1. Модификаторы операторов m// и s///

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

  • i - игнорирует различие между заглавными и строчными буквами. На этот модификатор влияет установка локали.
  • s - метасимвол "точка" совпадает со всеми символами, включая символ новой строки \n.
  • m - разрешает привязку метасимволам ^ и $ к промежуточным символам \n в тексте. В этом случае метасимвол ^ совпадает не только в начале текста, как якорь \A, но и после каждого символа \n, который не стоит в самом конце текста. А метасимвол $ начинает совпадать не только в самом конце текста и перед \n, стоящим в самом конце текста, как якорь \Z, но также после каждого символа \n, который не стоит в самом конце текста.
  • x - игнорирует пробельные символы в регулярном выражении, разрешает использовать внутри шаблона комментарии.
  • g - поиск и замена выполняются глобально (в неявном цикле). Подробности мы рассмотрим.
  • c - работает только с модификатором g и только с оператором m//, который применяется в скалярном контексте. Запрещает сбрасывать текущую позицию поиска, когда не удалось найти очередного совпадения. Подробности будут далее.
  • o - шаблон с этим модификатором однократной компиляции транслируется один раз во время компиляции всей программы. Здесь дело в том, что внутри регулярного выражения могут встречаться переменные, которые будут интерполироваться, т.е. их значения будут подставляться в шаблон в качестве литералов. (Не путайте эти переменные с теми, которые присутствуют в исполняемом коде Perl, такие переменные не интерполируются.) Если интерпретатор видит, что происходит интерполяция переменных, то он каждый раз перед использованием этого оператора поиска/замены будет транслировать регулярное выражение во внутреннее представление (байт код). Это может отнять много времени, если такое выражение используется в цикле. Но если вы уверены, что интерполируемая переменная (или массив) не меняет своего значения, то можете поставить к такому регулярному выражению модификатор o, чтобы избежать его многократной компиляции. Этот модификатор может стоять только после всего регулярного выражения.
  • 4.2. Алгоритм работы операторов m// и s///

    В языке Perl есть такое понятие - контекст выражения. Операторы могут возвращать значение, которое зависит от того, чему оно присваивается. Если оно присваивается скалярной переменной, то возвращаемое значение может быть одно, а если присвоение происходит массиву, то возвращаться может уже список значений. Таким является оператор m//. Например, если мы используем его в условии условного оператора:

    if (m/…/) { … }

    то это скалярный контекст, т.к. в условии ожидается одно значение, которое трактуется как истина или ложь. А если написать

    my @m=m/…/;

    то это списковый контекст, в котором оператор m// возвратит список значений. Оператор print является списковым, т.к. он ожидает список в качестве своего аргумента, поэтому, если написать

    print m/…/;

    то это также будет использование оператора поиска в списковом контексте.

    4.2.1. Оператор m// в режиме однократного поиска и в скалярном контексте

    В режиме однократного поиска (т.е. без модификатора g ) и в скалярном контексте оператор m// возвращает логическое значение: целое число 1, если поиск оказался успешным, или пустую строку в случае неудачи. Если в шаблоне имелись захватывающие скобки, то при успешном поиске создаются нумерованные переменные $1, $2, …, которые содержат соответствующие фрагменты захваченного текста. Если поиск оказался неудачным, то эти переменные хранят последнее состояние от предыдущего оператора поиска или замены.

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

  • $ - копия текста, совпавшего со всем регулярным выражением. Если вы заключите все регулярное выражение в круглые скобки: m/(…)/, то в случае успеха в переменной $1 получите то же самое значение.
  • $` - копия текста, предшествующего началу совпадения (т.е. текст, который был слева от совпадения).
  • $' - копия текста, следующего после совпадения (т.е. расположенного справа от совпавшего текста). После успешного совпадения текст "$`$$'" представляет из себя копию исходного текста. Есть малозначительное исключение: если оператор m// был успешно применен к неопределенной переменной, то эти специальные переменные будут иметь пустые значения.
  • $+ - копия переменной $1 или $2… с максимальным номером, которой было присвоено значение. Если в регулярном выражении нет сохраняющих скобок или они не задействованы в совпадении, то эта переменная получает неопределенное значение.
  • $^N - копия переменной $1 или $2…, которая соответствует последней только что закрытой паре скобок на момент использования этой переменной. Если в регулярном выражении нет сохраняющих скобок или они не задействованы в совпадении, то эта переменная получает неопределенное значение.
  • @ - массив начальных смещений в целевом тексте. $-[0] хранит смещение начала совпадения от начала текста для всего регулярного выражения. $-[1] хранит смещение начала совпадения от начала текста для фрагмента текста, захваченного в переменную $1, $-[2] - для переменной $2 и т.д.
  • @+ - аналогичный массив конечных смещений в целевом тексте. Т.е. смещений символов, следующих за конечными символами совпадения во всем тексте, в переменной $1, $2 и т.д. Имеет место соотношение substr($text,$-[0],$+[0]-$-[0]) эквивалентно $, substr($text,$-[1],$+[1]-$-[1]) эквивалентно $1 и т.д.
  • Теперь буду давать пояснения к этому списку. Во-первых, имейте в виду, что переменная $0, похожая на нумерованную, не имеет отношения к регулярным выражениям, а хранит имя сценария Perl, запущенного на выполнение.

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

    $text =~ /(…)|(…)|(…)/;

    Мы знаем, что совпадение может быть не более чем с одной альтернативой, и хотим получить фрагмент текста, совпавшего с какой-либо альтернативой. Не будь переменной $+, нам пришлось бы перебирать переменные $1, $2,… и определять, какая из них имеет определенное значение.

    Для понимания отличия переменной $+ от $^N рассмотрим такой пример:

    print "\$+=$+, \$^N=$^N" if "abc" =~ /(a(bc))/;

    На печати окажется строка

    $+=bc, $^N=abc

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

    Использовать значения переменных $+ и $^N можно также в коде Perl внутри регулярного выражения. Эти переменные корректируются после того, как закроется очередная захватывающая скобка. Например:

    print "\$+=$+, \$^N=$^N"
     if "abcd" =~ /(a(bc)(d)(?{ print "\$+=$+, \$^N=$^N\n" }))/;

    Здесь выводятся значения переменных $+ и $^N после третьей скобки внутри регулярного выражения, а также после того, как оператор поиска отработал. В итоге получается результат:

    $+=d, $^N=d
    $+=d, $^N=abcd

    $+ в обоих случаях относится к нумерованной переменной с максимальным номером - $3, а переменная $^N внутри регулярного выражения копирует переменную $3, а вне регулярного выражения является копией переменной $1.

    4.2.2. Оператор m// в списковом контексте без модификатора g

    Рассмотрим работу оператора m// в списковом контексте в режиме однократного поиска (т.е без модификатора g ). Возвращаемое значение зависит от того, есть ли в шаблоне захватывающие скобки. Если есть хотя бы одна пара захватывающих скобок, то в результате будет возвращен список из значений всех нумерованных переменных по числу использованных в регулярном выражении захватывающих скобок: ($1, $2, $3, …).

    Сами эти нумерованные переменные также создаются. В этом варианте оператор m// обычно применяется, когда надо извлечь из строки заранее известное число групп переменная=значение. Например:

    my $url='var1=value1var2=value2';
    my @pairs=$url =~ /(\w+)=(\w+)(\w+)=(\w+)/;
    print join ',',@pairs;

    На печать выйдет строка

    var1,value1,var2,value2

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

    В случае неудачного поиска в обоих случаях возвращается пустой список.

    Вот пример идиомы для применения оператора m// в списковом контексте:

    my $date='2007/3/12';
    if (my($year,$month,$day)=$date =~ m!^(\d+)/(\d+)/(\d+)$!)
     { print "Year=$1, month=$2, day=$3";
     } else
      { print 'Not found!';
      }

    На печать выведется

    Year=2007, month=3, day=12

    Мы присваиваем вовращаемое значение списку, поэтому применяется списковый контекст оператора поиска. Затем в операторе if этот список рассматривается в скалярном контексте, что дает число элементов этого списка. Полученный результат используется для проверки успешности выполнения оператора поиска.

    4.2.3. Оператор m// в скалярном контексте с модификатором g

    Оператор поиска в скалярном контексте и с модификатором g ведет себя особым образом: переменная, к которой применяется поиск, будет неявно хранить позицию, на которой остановился последний поиск оператором m// в этой переменной. При очередном применении к этой переменной оператора поиска с модификатором g (в списковом или скалярном контексте) поиск продолжится не с начала текста, а с той позиции, на которой он остановился в последний раз. Это позволяет применить к одной и той же переменной несколько операторов поиска и извлекать из текста элементы один за другим. Эта позиция, на которой остановился оператор m//g в последний раз, может быть получена при обращении к функции pos с тем же аргументом, к которому применялся этот поиск.

    Вот пример:

    $_='1234567890';
    /(\d)(\d)/g;
    print "$1,$2\n";
    my @a=/(\d)(\d)/g;
    print join ',',@a;

    На печать выйдут строки

    1,2
    3,4,5,6,7,8,9,0

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

    В случае применения оператора поиска к одной и той же константе:

    while ('abcd'=~/(\w)/g) { print $1 }

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

    abcd

    4.2.4. Оператор m// в списковом контексте с модификатором g

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

    Пример:

    my $text='123 234 345 456';
    my @m=$text =~ /(\d+)(\s+)/g;
    print @m;

    Будет напечатана строка

    123 234 345 456

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

    my $text='123 234 345 456';
    my @m=$text =~ /\d+/g;
    print join ',',@m;

    Будет напечатано

    123,234,345,456

    4.2.5. Работа оператор s/// с модификатором g и без него

    Оператор замены s/// без модификатора g ищет в заданной переменной прототип регулярного выражения и в случае нахождения заменяет его на выражение для замены, после чего заканчивает свою работу. При наличии модификатора g замена производится столько раз, сколько раз был найден фрагмент текста, соответствующий регулярному выражению. В результате все найденные фрагменты будут заменены на заданное выражение. В результате возвращается число произведенных замен или пустая строка (или соответствующий список в случае спискового контекста). Например:

    my $text='123 234 345 456';
    $text =~ s/\d+/a/;
    print $text;

    Получим строку

    a 234 345 456

    Еще пример:

    my $text='123 234 345 456';
    $text =~ s/\d+/reverse $/ge;
    print $text;

    Здесь получим такой результат:

    321 432 543 654

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

    $text =~ s/(\d+)/reverse $1/ge;

    В операторах m// и s/// специальные переменные $1, $2, …, $, $`, $' и т.д. для каждой итерации, обусловленной модификатором g, создаются заново (локализуются). В результате при замене используются нужные значения этих переменных, которые возникли при последней итерации поиска.

    4.3. Предварительная обработка регулярных выражений

    Регулярные выражения в операторах m// и s/// обрабатываются особым образом. Вообще говоря, они "обрабатываются как строка в кавычках с учетом специфики регулярных выражений". Но если в операторе поиска/подстановки регулярное выражение ограничено апострофами, то такое регулярное выражение, как обычно пишут, "обрабатывается как строка в апострофах", т.е. интерполяция переменных в нем не производится. Здесь надо уточнить, что такие эскейп-последовательности, как \r, \n, \t, все равно работают в регулярном выражении, которое ограничено апострофами, хотя в строке, ограниченной апострофами, они не работают. В этом - специфика регулярных выражений.

    В части замены оператора s/// действует то же правило, за тем исключением, когда там стоит код Perl (есть модификатор e ). В строке, ограниченной апострофами, имеются только два метасимвола: апостроф и обратный слэш. Здесь замечу, что в строке замены, которая ограничена апострофами, эскейп-последовательности \r, \n, \t, … не работают. В случае, если регулярное выражение не ограничено апострофами, в нем символы $ и @ являются метасимволами. Но символ $ используется как якорь для конца строки, не получается ли здесь конфликта? Этот вопрос мы рассмотрим позднее.

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

    Замечу еще, что внутри классов переменные тоже интерполируются. Для закрепления этого материала рассмотрим такие примеры:

    $_='abc';
    my $s='c';
    print '1' if /^ab$s$/;

    Будет напечатана единица. После интерполяции регулярное выражение станет таким:

    /^abс$/

    Другой пример:

    $_='abc';
    my $s='ab';
    print /[$s]/g;

    Напечатается ab. Это регулярное выражение эквивалентно такому:

    /[ab]/g

    Класс совпал два раза и захватил на первой итерации букву a, а на второй - букву b.

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

    После интерполяции переменных происходит обработка конструкций изменения регистра символов /L, /l, /U, /u, а также конструкций вставки литерального текста \Q…\E.

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

    Вернуться к учебному плану