Памятка по регулярным выражениям. Регулярные выражения в perl

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

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

Операторы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Память

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

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

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

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

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

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

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

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

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

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

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

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

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

6.4.1. Синтаксис регулярных выражений

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

/pattern/modifiers

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

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

Таблица 6.9. Метасимволы в регулярных выражениях
Символ Описание
\ Для символов, которые обычно трактуются буквально, означает, что следующий символ является метасимволом. Например, /n/ соответствует букве n, а /\n/ соответствует символу перевода строки.
Для метасимволов означает, что символ должен пониматься буквально. Например, /^/ означает начало строки, а /\^/ соответствует просто символу ^. /\\/ соответствует обратной косой черте \.
^ Соответствует началу строки (ср. модификатор ).
$ Соответствует концу строки (ср. модификатор ).
. Соответствует любому символу, кроме разрыва строки (ср. модификатор ).
* Соответствует повторению предыдущего символа нуль или более раз.
+ Соответствует повторению предыдущего символа один или более раз.
? Соответствует повторению предыдущего символа нуль или один раз.
(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 Соответствует только началу строки, даже с модификатором .
\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 Соответствует только концу строки, даже с модификатором .
\Z Соответствует только концу строки или разрыву строк в конце строки, даже с модификатором .
\ 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}/ эквивалентно /♣/.

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

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

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

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

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

[:class:]

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

\p{class}

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

Таблица 6.10. Классы символов
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}

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

Использование круглых скобок в регулярном выражении приводит к тому, что подстрока, соответствующая образцу в скобках, запоминается в специальном буфере. Для доступа к n -ной запомненной подстроке внутри регулярного выражения используется конструкция \ n , а вне него — $ n , где n может принимать любые значения, начиная с 1. Однако, следует помнить, что PERL использует выражения \10 , \11 и т. д. как синонимы для восьмеричных кодов символов \010 , \011 и т. д. Неоднозначность здесь разрешается так. Символ \10 считается обращением к 10-й запомненной подстроке, если перед ним в регулярном выражении стоит не менее десяти левых круглых скобок; в противном случае, это символ с восьмеричным кодом 10. Метасимволы \1 , … \9 всегда считаются обращениями к запомненным подстрокам. Примеры:

If (/(.)\1/) { # ищем первый повторяющийся символ print ""$1" - первый повторяющийся символ\n"; } if (/Time: (..):(..):(..)/) { # извлекаем компоненты времени $hours = $1; $minutes = $2; $seconds = $3; }

Помимо переменных $1 , $2 , … есть еще несколько специальных переменных, в которых сохраняются результаты последней операции с регулярным выражением, а именно:

Приведем пример:

"AAA111BBB222"=~/(\d+)/; print "$`\n"; # AAA print "$&\n"; # 111 print "$"\n"; # BBB222 print "$+\n"; # 111

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

6.4.5. Расширенные образцы

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

(?#text ) Комментарий. Вся конструкция игнорируется. (?modifiers -modifiers ) Включает или выключает заданные . Модификаторы, стоящие до символа - , включаются, стоящие после него — выключаются. Пример:

If (/aaa/) { … } # сопоставление с учетом регистра if (/(?i)aaa/) { … } # сопоставление без учета регистра

(?:pattern ) (?modifiers -modifiers :pattern ) Позволяет группировать подвыражения регулярного выражения без запоминания найденного соответствия. Вторая форма дополнительно включает или выключает заданные . Например, выражение /ко(?:т|шка)/ — это краткая запись выражения /кот|кошка/ . (?=pattern ) Соответствие с заглядыванием вперед без запоминания найденного соответствия. Например, выражение /Windows (?=95|98|NT|2000)/ соответствует "Windows" в строке "Windows 98", но не соответствует в строке "Windows 3.1". После сопоставления поиск продолжается с позиции, следующей за найденным соответствием, без учета заглядывания вперед. (?!pattern ) Несоответствие с заглядыванием вперед без запоминания найденного соответствия. Например, выражение /Windows (?!95|98|NT|2000)/ соответствует "Windows" в строке "Windows 3.1", но не соответствует в строке "Windows 98". После сопоставления поиск продолжается с позиции, следующей за найденным соответствием, без учета заглядывания вперед. (?<=pattern ) Соответствие с заглядыванием назад без запоминания найденного соответствия. Например, выражение /(?<=\t)\w+/ соответствует слову, следующему за символом табуляции, и символ табуляции не включается в $& . Фрагмент, соответствующий заглядыванию назад, должен иметь фиксированную ширину. (?pattern ) Несоответствие с заглядыванием назад без запоминания найденного соответствия. Например, выражение /(?6.4.6. Операции с регулярными выражениями

До сих пор мы заключали регулярные выражения в символы // . На самом деле символы-ограничители регулярного выражения определяются q-операцией , которую мы к ним применяем. В этом разделе подробно описаны все операции языка PERL с регулярными выражениями.

6.4.6.1. Сопоставление с образцом

Синтаксис : /pattern /modifiers m/pattern /modifiers

pattern и возвращает истину или ложь в зависимости от результата сопоставления. Сопоставляемая строка задается левым операндом операции =~ или!~ , например:

$mynumber = "12345"; if ($mynumber =~ /^\d+$/) { # если строка $mynumber состоит из десятичных цифр, то… ... }

Если строка не задана, то производится сопоставление с содержимым специальной переменной $_ . В частности, предыдущий пример можно переписать так:

$_ = "12345"; if (/^\d+$/) { ... }

Если регулярное выражение заключено в // , то начальное m необязательно. Конструкция с начальным m позволяет использовать в качестве ограничителей регулярного выражения любые символы, допустимые в q-операциях. Полезные частные случаи:

Если pattern

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

($w1, $w2, $rest) = ($x =~ /^(\S+)\s+(\S+)\s*(.*)/);

заносит в переменную $w1 первое слово строки $x , в переменную $w2 ее второе слово, а в переменную $rest — остаток этой строки.

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

$_ = "12:23:45"; @result = /\d+/g; foreach $elem (@result) { print "$elem\n"; }

выведет на экран строки 12 , 23 и 45 .

В скалярном контексте сопоставление с модификатором g каждый раз ищет следующее соответствие образцу и возвращает истину или ложь в зависимости от результата поиска. Позиция в строке после последнего сопоставления может быть считана или изменена функцией pos() . Неудачный поиск обычно сбрасывает позицию поиска в нуль, но мы можем избежать этого, добавив модификатор c . Изменение строки также приводит к сбросу позиции поиска в ней.

Дополнительные возможности предоставляет метасимвол \G , который имеет смысл только в сочетании с модификатором g . Этот метасимвол соответствует текущей позиции поиска в строке. Использование конструкции m/\G…/gc удобно, в частности, для написания лексических анализаторов, выполняющих различные действия для встреченных в анализируемом тексте лексем. Следующий пример

$_ = "Word1, word2, and 12345."; LOOP: { print("number "), redo LOOP if /\G\d+\b[,.;]?\s*/gc; print("word "), redo LOOP if /\G+\b[,.;]?\s*/gc; print("unknown "), redo LOOP if /\G[^A-Za-z0-9]+/gc; }

выведет на экран строку word word word number .

6.4.6.2. Единственное сопоставление с образцом

Синтаксис : ?pattern ? m?pattern ?

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

While (<>) { if (?^$?) { ... # обработать первую пустую строку файла } } continue { reset if eof; # сбросить статус?? для следующего файла }

6.4.6.3. Создание регулярного выражения

Синтаксис : qr/string /modifiers

Эта конструкция создает регулярное выражение с текстом string и модификаторами modifiers и компилирует его. Если ограничителями являются символы "" , то интерполяция строки string o

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

$re = qr/\d+/; $string =~ /\s*${re}\s*/; # включение в другое регулярное выражение $string =~ $re; # самостоятельное использование $string =~ /$re/; # то же самое $re = qr/$header/is; s/$re/text/; # то же, что s/$header/text/is

6.4.6.4. Подстановка

Синтаксис : s/pattern /string /modifiers

Эта операция сопоставляет заданную строку с образцом pattern и заменяет найденные фрагменты на строку string . Она возвращает количество произведенных замен или ложь (точнее, пустую строку), если сопоставление закончилось неудачей. Сопоставляемая строка задается левым операндом операции =~ или!~ . Она должна быть скалярной переменной, элементом массива или элементом ассоциативного массива, например:

$path = "/usr/bin/perl"; $path =~ s|/usr/bin|/usr/local/bin|;

$_ = "/usr/bin/perl"; s|/usr/bin|/usr/local/bin|;

Помимо стандартных, здесь могут употребляться следующие модификаторы:

pattern string должен иметь собственную пару ограничителей, например s(foo) или s/bar/ .

Если ограничителями являются символы "" , то интерполяция строки pattern не производится. В остальных случаях происходит интерполяция образца и если он содержит переменные, то при каждом сопоставлении производится его компиляция. Чтобы избежать этого, используйте модификатор o (разумеется, если вы уверены, что значения переменных, входящих в образец, остаются неизменными).

Если pattern является пустой строкой, то вместо него используется последнее успешно сопоставленное регулярное выражение.

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

Модификатор e указывает, что string является выражением. В этом случае к string сначала применяется функция eval() , а затем производится подстановка. Пример:

$_ = "123"; s/\d+/$&*2/e; # $_ = "246" s/\d/$&*2/eg; # то же самое

Приведем еще несколько типичных примеров использования операции подстановки. Удаление комментариев вида /*…*/ из текста Java- или C-программы:

$program =~ s { /\* # Начало комментария.*? # Минимальное количество символов \*/ # Конец комментария }gsx;

Удаление начальных и конечных пробелов в строке $var:

For ($var) { s/^\s+//; s/\s+$//; }

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

S/([^ ]*) *([^ ]*)/$2 $1/;

Замена табуляций на пробелы с выравниванием по колонкам, кратным восьми:

1 while s/\t+/" " x (length($&)*8 - length($`)%8)/e;

6.4.6.5. Транслитерация

Синтаксис : tr/list1 /list2 /modifiers y/list1 /list2 /modifiers

Транслитерация состоит в замене всех символов из списка list1 соответствующими символами из списка list2 . Она возвращает количество замененных или удаленных символов. Списки должны состоять из отдельных символов и/или диапазонов вида a-z . Преобразуемая строка задается левым операндом операции =~ или!~ . Она должна быть скалярной переменной, элементом массива или элементом ассоциативного массива, например:

$test = "ABCDEabcde"; $test =~ tr/A-Z/a-z/; # замена строчных букв на прописные

Если строка не задана, то операция подстановки производится над специальной переменной $_ . В частности, предыдущий пример можно переписать так:

$_ = "ABCDEabcde"; tr/A-Z/a-z/;

Мы можем использовать вместо // любой символ, допустимый в q-операциях. Если list1 заключен в парные скобки, то list2 должен иметь собственную пару ограничителей, например tr(A-Z) или tr/a-z/ .

Обычно эта операция называется tr . Синоним y введен для фанатиков редактора sed и используется только ими. Транслитерация поддерживает следующие модификаторы:

Модификатор c вызывает транслитерацию всех символов, не входящих в список list1 . Например, операция tr/a-zA-Z/ /c заменит все символы, не являющиеся латинскими буквами, на пробелы.

По умолчанию, если list2 короче, чем list1 , он дополняется последним своим символом, а если он пуст, то принимается равным list1 (это удобно для подсчета количества символов определенного класса в строке). Модификатор d изменяет эти правила: все символы из list1 , которым нет соответствия в list2 , удаляются из строки. Например, операция tr/a-zA-Z//cd удалит из строки все символы, не являющиеся латинскими буквами.

Модификатор s удаляет повторы: если несколько символов подряд заменились на один и тот же символ, то будет оставлено только один экземпляр этого символа. Например, операция tr/ / /s удаляет в строке повторяющиеся пробелы.

Модификаторы C и U предназначены для перекодировки символов из системной кодировки в UTF-8 и обратно. Первый из них указывает на исходную кодировку, а второй — на кодировку результата. Например, tr/\0-\xFF//CU перекодирует строку из системной кодировки в UTF-8, а tr/\0-\xFF//UC выполнит обратную перекодировку.

Транслитерация производится без интерполяции списков символов, поэтому для использования в ней переменных необходимо вызвать функцию eval() , например.

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

perlre - регулярные выражения Perl
В этом руководстве описан синтаксис регулярных выражений в языке Perl. Описание того, как практически использовать регулярные выражения в операциях сопоставления с образцом, а также разнообразные примеры на эту тему можно найти в разделах m// и s/// на странице справочного руководства perlop .

ОПИСАНИЕ регулярных выражений

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

Последний обычно называют "модификатор /x", хотя рассматриваемый разделитель может и не быть косой. Фактически, любой из этих модификаторов может быть встроен в регулярное выражение с помощью новой конструкции (?...) . См. ниже.

Сам модификатор /x требует немного более подробного рассмотрения. Он заставляет синтаксический анализатор регулярных выражений игнорировать пробельные символы, не замаскированные обратной косой и не входящие в класс символов. Это можно использовать для разбиения регулярного выражения на (немного) более понятные части. Символ # также рассматривается как метасимвол начала комментария, как в остальном коде на Perl. Взятые вместе, эти возможности делают Perl 5 намного более удобочитаемым языком. См. пример кода для удаления комментариев в программе на C на странице справочного руководства perlop .

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

Шаблоны, используемые при сопоставлении с образцом, являются регулярными выражениями типа используемых в версии 8 библиотеки regexp . (Фактически, соответствующие функции являются производными (хотя и весьма далекими) от свободно распространяемой реализации версии 8, которую выполнил Henry Spencer.) Подробнее см. раздел "Регулярные выражения версии 8" .

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

По умолчанию, символ "^ " гарантированно соответствует только началу строки, а символ "$ " - только концу строки (или позиции перед символом перевода строки в конце), причем Perl выполняет ряд оптимизаций исходя из предположения, что буфер содержит только одну строку. Встроенным переводам строк не будут соответствовать метасимволы "^ " или "$ ". Может, однако, понадобиться рассматривать буфер как многострочный, так чтобы "^ " соответствовал позиции после символа перевода строки в буфере, а "$ " - позиции перед символом перевода строки. За счет незначительного повышения накладных расходов это можно сделать с помощью модификатора /m в операторе сопоставления с образцом. (Старые программы для этого устанавливали $* , но такая практика теряет смысл в Perl 5.)

Чтобы упростить многострочные подстановки, символ ". " никогда не соответствует символу перевода строки, если только не используется модификатор /s , сообщающий Perl о необходимости рассматривать буфер как однострочный, - даже если в нем несколько строк. Модификатор /s также отменяет установку $* , если используется (неудачный) старый код, устанавливающий его в другом модуле.

Распознаются следующие стандартные квантификаторы :

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

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

Учтите, что изменяется не значение квантификаторов, а "вес" , - они будут сопоставляться с наименьшей возможной подстрокой :

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

\t табуляция
\n перевод строки
\r возврат каретки
\f form feed
\a звуковой сигнал
\e escape (вспомните troff )
\033 восьмеричный символ (вспомните PDP-11)
\x1B шестнадцатеричный символ
\c[ управляющий символ
\l перевести следующий символ в нижний регистр (вспомните vi )
\u перевести следующий символ в верхний регистр (вспомните vi )
\L переводить в нижний регистр до \E (вспомните vi )
\U переводить в верхний регистр до \E (вспомните vi )
\E конец изменения регистра символов (вспомните vi )
\Q маскировать метасимволы regexp до \E

Кроме того, Perl определяет следующие метасимволы :

Учтите, что \w соответствует одному алфавитно-цифровому символу, а не целому слову. Чтобы указать соответствие слову, необходимо использовать \w+ . Метасимволы \w , \W , \s , \S , \d и \D можно использовать при задании классов символов (но не в качестве одной из границ диапазона).

Perl определяет следующие утверждения нулевой длины (zero-width assertions):

Граница слова (\b ) определяется как точка между двумя символами, с одной стороны от которой находится \w , а с другой - \W (в любом порядке), считая воображаемые символы начала и конца строки соответствующими \W . (Внутри классов символов \b представляет забой - backspace, а не границу слова.) Метасимволы \A и \Z аналогичны "^ " и "$ ", но не будут сопоставляться несколько раз при использовании модификатора /m , тогда как "^ " и "$ " будут сопоставляться с границей каждой внутренней строки. Чтобы указать соответствие с реальным концом строки, не исключая символ перевода строки, можно использовать \Z(?!\n) .

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

Можно использовать любое количество скобок. Если имеется более 9 подстрок, переменные $10 , $11 , ... будут ссылаться на соответствующую подстроку. В шаблоне \10 , \11 и т.д. ссылаются на уже сопоставленные подстроки, если их уже было столько до этой обратной ссылки. В противном случае (для обратной совместимости) \10 совпадает с \010 , или символом забоя, а \11 совпадает с \011 , символом табуляции. И так далее. (Последовательности от \1 до \9 всегда рассматриваются как обратные ссылки.)

$+ возвращает то, с чем сопоставилась последняя конструкция в скобках. $& возвращает всю сопоставившуюся строку. (Раньше для этого использовался $0 , но больше не используется.) $` возвращает все, что идет до начала сопоставившейся строки. $" возвращает все, что идет после сопоставившейся строки. Примеры:

S/^([^ ]*) *([^ ]*)/$2 $1/; # поменять местами # два первых слова if (/Time: (..):(..):(..)/) { $hours = $1; $minutes = $2; $seconds = $3; }

Обратите внимание, что все метасимволы, предваряемые обратной косой, в Perl - алфавитно-цифровые, например, \b , \w , \n . В отличие от некоторых языков регулярных выражений, здесь обратная косая не предваряет метасимволы, не являющиеся алфавитно-цифровыми. Поэтому все конструкции вида \\ , \(, \) , \< , \> , \{ или \} всегда интерпретируются как литеральные символы, а не как метасимволы. Это упрощает маскировку строки, которую необходимо использовать в качестве шаблона, но которая, как вы опасаетесь, может содержать метасимволы. Просто замаскируйте все не алфавитно-цифровые символы:

$pattern =~ s/(\W)/\\$1/g;

Для этого можно также использовать встроенную функцию quotemeta() . Еще проще замаскировать метасимволы прямо в операторе сопоставления можно следующим образом

/$unquoted\Q$quoted\E$unquoted/

Perl 5 определяет последовательный синтаксис расширений для регулярных выражений. Для этого используется пара круглых скобок, первым символом в которых указан знак вопроса (в Perl 4 это было синтаксической ошибкой). Символ после знака вопроса задает функцию расширения. Поддерживается несколько расширений:

(?#text)

Комментарий. Текст игнорируется. Если использован переключатель /x для вставки форматирующих пробелов, достаточно указать просто # .

(?:regexp) Группирует элементы аналогично "() ", но не создает обратных ссылок, как "() ". Поэтому split(/\b(?:a|b|c)\b/)

аналогично

Split(/\b(a|b|c)\b/)

но не порождает дополнительные поля.

(?=regexp) Положительный просмотр вперед нулевой длины. Например, /\w+(?=\t)/ соответствует слову, после которого идет символ табуляции, но табуляция не включается в $& .
(?!regexp) Отрицательный просмотр вперед нулевой длины. Например, /foo(?!bar)/ соответствует любому вхождению "foo ", за которым не идет "bar ". Учтите, однако, что просмотр вперед и просмотр назад - НЕ одно и то же. Нельзя использовать эту конструкцию для поиска назад: /(?!foo)bar/ не найдет вхождение "bar ", перед которым не идет "foo ". Так происходит потому, что (?!foo) означает, что дальше не должна идти строка "foo " -- а она и не идет, идет "bar ", поэтому "foobar " будет соответствовать этому шаблону. Необходимо задавать что-то вроде /(?foo)...bar/ . "Вроде" - потому, что перед "bar " может и не быть трех символов. Этот случай можно охватить следующим образом: /(?:(?!foo)...|^..?)bar/ . Иногда все же проще написать: if (/foo/ && $` =~ /bar$/)
(?imsx) Один или несколько встроенных модификаторов сопоставления с образцом. Это особенно полезно для шаблонов, заданных в отдельной таблице, когда некоторые из них должны учитывать регистр символов, а другие - нет. Для учитывающих регистр символов достаточно просто включить (?i) перед шаблоном. Например: $pattern = "foobar"; if (/$pattern/i) # более гибкий способ: $pattern = "(?i)foobar";
if (/$pattern/)

Знак вопроса для этого и новой конструкции минимального сопоставления был выбран потому, что 1) знак вопроса редко встречался в прежних регулярных выражениях и 2) когда вы видите знак вопроса, надо остановиться и "спросить" себя, что же на самом деле происходит. Это психология...

Регулярные выражения: Поиск с возвратом

Фундаментальное свойство сопоставления регулярных выражений связано с понятием, которое называется поиск с возвратом (backtracking) и используется (при необходимости) всеми квантификаторами регулярных выражений, а именно * , *? , + , +? , {n,m} и {n,m}? .

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

Вот пример поиска с возвратом: предположим, необходимо найти слово, идущее после "foo " в строке "Food is on the foo table. ":

$_ = "Food is on the foo table."; if (/\b(foo)\s+(\w+)/i) { print "$2 follows $1.\n"; }

При выполнении сопоставления для первой части регулярного выражения (\b(foo) ) найдется возможное соответствие прямо в начале строки, при этом в $1 будет помещено значение "Foo ". Однако, как только механизм сопоставления увидит, что после сохраненного в $1 значения "Foo " нет пробела, он поймет свою ошибку и начнет снова со следующего символа после неудавшегося сопоставления an. В этот раз он пройдет до следующего вхождения "foo ". Все регулярное выражение в целом теперь сопоставляется и будет получен ожидаемый результат, "table follows foo. ".

Иногда минимальное сопоставление может оказаться очень полезным. Предположим, необходимо найти все, что идет между строками "foo " и "bar ". Сразу можно написать что-то вроде:

$_ = "The food is under the bar in the barn."; if (/foo(.*)bar/) { print "got <$1>\n"; }

Что, возможно, неожиданно, выдает:

Got

Так произошло потому, что шаблон .* был жадным, вот вы и получили все от первого "foo " до последнего "bar ". В этом случае более эффективно использовать минимальное сопоставление, гарантирующее, что вы получите текст между "foo " и первым же вхождением "bar " после него.

If (/foo(.*?)bar/) { print "got <$1>\n" } got

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

$_ = "I have 2 numbers: 53147";
if (/(.*)(\d*)/) { #Ошибка! print "Beginning is <$1>, number is <$2>.\n"; }

Это вообще не сработает, поскольку шаблон .* был жадным и поглотил всю строку. Поскольку \d* может соответствовать пустой строке, все регулярное выражение в целом успешно сопоставляется.

Beginning is , number is <>.

Вот еще несколько вариантов, большинство из которых не сработает:

$_ = "I have 2 numbers: 53147"; @pats = qw{ (.*)(\d*) (.*)(\d+) (.*?)(\d*) (.*?)(\d+) (.*)(\d+)$ (.*?)(\d+)$ (.*)\b(\d+)$ (.*\D)(\d+)$ }; for $pat (@pats) { printf "%-12s ", $pat; if (/$pat/) { print "<$1> <$2>\n"; } else { print "FAIL\n"; } } В результате будет выдано:
(.*)(\d*) <> (.*)(\d+) <7> (.*?)(\d*) <> <> (.*?)(\d+) <2> (.*)(\d+)$ <7> (.*?)(\d+)$ <53147> (.*)\b(\d+)$ <53147> (.*\D)(\d+)$ <53147>

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

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

$_ = "ABC123"; if (/^\D*(?!123)/) { # Ошибка! print "Yup, no 123 in $_\n"; }

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

$x = "ABC123" ; $y = "ABC445" ; print "1: got $1\n" if $x =~ /^(ABC)(?!123)/ ; print "2: got $1\n" if $y =~ /^(ABC)(?!123)/ ; print "3: got $1\n" if $x =~ /^(\D*)(?!123)/ ; print "4: got $1\n" if $y =~ /^(\D*)(?!123)/ ;

Будет выдано

2: got ABC 3: got AB 4: got ABC

Вы могли ожидать, что проверка 3 не сработает, поскольку она кажется более универсальной версией 1. Важное различие между ними состоит в том, что проверка 3 содержит квантификатор (\D*) и поэтому может использовать поиск с возвратом, тогда как проверка 1 - нет. На самом деле вы спрашиваете: "Правда ли, что в начале $x , после 0 или более не цифр, идет нечто, отличающееся от 123 ?". Если механизм сопоставления позволит \D* расшириться до "ABC ", весь шаблон в целом не сопоставится. Поисковая машина первоначально сопоставит \D* с "ABC ". Затем она попытается сопоставить (?!123) c "123 ", что, конечно, невозможно. Но поскольку в регулярном выражении использован квантификатор (\D*) , поисковая машина может вернуться и поискать другое сопоставление в надежде найти сопоставить все регулярное выражение в целом.

Теперь, поскольку сопоставление шаблона так желанно для поисковой машины, она использует стандартный возврат и повторную попытку regexp (backoff-and-retry) и позволяет на это раз \D* расшириться только до "AB ". Теперь и в самом деле имеется нечто после "AB ", что не совпадает с "123 ". Это "C123 ", что вполне устраивает.

Справиться с эти можно, используя совместно утверждение и отрицание. Мы скажем, что после первой части в $1 должна идти цифра, но там должно идти нечто, отличное от "123 ". Помните, что просмотры вперед - это выражения нулевой длины -- при сопоставлении выполняется только проверка, но не берется часть строки. После таких изменений будет получен желаемый результат; т.е. в случае 5 - неудача, а в случае 6 - успех:

Print "5: got $1\n" if $x =~ /^(\D*)(?=\d)(?!123)/ ; print "6: got $1\n" if $y =~ /^(\D*)(?=\d)(?!123)/ ; 6: got ABC

Другими словами, два утверждения нулевой длины (zero-width assertions), идущие подряд, работают так, как если бы проверялась их конъюнкция, так же, как и при использовании любых встроенных утверждений: шаблон /^$/ сопоставляется, только если вы находитесь в начале строки И в конце строки одновременно. Более глубокое основание этого - в том, что соседство в регулярных выражениях всегда означает И, кроме явного указания ИЛИ с помощью вертикальной черты. /ab/ означает сопоставить "a " И (затем) сопоставить "b ", хотя попытки сопоставления и делаются в разных позициях, т.к. "a " - утверждение не нулевой длины, но длины один.

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

/((a{0,5}){0,5}){0,5}/

А если использовать * вместо ограничения количества вхождений от 0 до 5, сопоставление будет выполняться бесконечно -- или пока не исчерпается место в стеке.

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

Если вам не знакомы "стандартные" функции библиотеки regexp версии 8, вот правила сопоставления с образцом, не описанные выше.

Любой одиночный символ сопоставляется с сами собой, если только это не метасимвол, имеющий специальное значение, описанное здесь или выше. Символы, обычно работающие как метасимволы, можно потребовать интерпретировать литерально, предваряя их символом "\ " (например, "\. " соответствует ". ", а не любому символу; "\\ " соответствует "\ "). Последовательность символов сопоставляется с такой же последовательностью символов в целевой строке, поэтому шаблон blurfl сопоставится с "blurfl " в целевой строке.

Можно задать класс символов, включив список символов в квадратные скобки , которые будут сопоставляться с любым из символов в списке. Если первый символ после "[ " - "^ ", класс сопоставляется с любым символом, не указанным в списке. В списке символ "- " используется для указания диапазона, так что a-z представляет все символы от "a " до "z ", включительно.

Символы можно задавать с использованием синтаксиса метасимволов, во многом аналогичного используемому в C: "\n " соответствует переводу строки, "\t " - табуляции, "\r " - возврату каретки, "\f " - form feed и т.д. В общем случае, \nnn , где nnn - это строка восьмеричных цифр, соответствует символу, значение кода ASCII для которого - nnn . Аналогично, \xnn , где nn - это шестнадцатеричные цифры, соответствует символу, значение кода ASCII для которого - nn . Выражение \cx соответствует символу ASCII control-x . Наконец, метасимвол ". " соответствует любому символу, кроме "\n " (если только не используется /s ).

Можно задавать набор альтернатив для шаблона, разделяя их метасимволом "| ", так что fee|fie|foe сопоставится с любой из подстрок "fee ", "fie " или "foe " в целевой строке (так же, как и f(e|i|o)e ). Учтите, что первая альтернатива включает все от последнего разделителя шаблона ("(", "[ " или от начала шаблона) до первого символа "| ", а последняя альтернатива включает все от последнего символа "| " до следующего разделителя шаблона. Поэтому альтернативы обычно берут в круглые скобки, чтобы не сомневаться, где они начинаются и заканчиваются. Учтите, однако, что в квадратных скобках "| " интерпретируется как литерал, поэтому если вы напишите , сопоставление произойдет только с .

В шаблоне можно выделять подшаблоны (путем взятия их в круглые скобки) для дальнейших ссылок и можно ссылаться обратно на n -й подшаблон в дальнейшем с помощью метасимвола \n . Подшаблоны нумеруются слева направо по открывающим круглым скобкам. Учтите, что обратная ссылка сопоставляется с тем, с чем сопоставился подшаблон в рассматриваемой строке, а не с правилами, задающими этот подшаблон. Поэтому (0|0x)\d*\s\1\d* сопоставится с "0x1234 0x4321 ", но не с "0x1234 01234 ", поскольку подшаблон 1 фактически сопоставился с "0x ", хотя правило 0|0x потенциально могло сопоставиться с начальным 0 во втором числе.

ПРЕДУПРЕЖДЕНИЕ о \1 и $1

Некоторые люди слишком привыкли писать вещи типа

$pattern =~ s/(\W)/\\\1/g;

Корни такой привычки восходят к правой части оператора замены в sed , но это плохая привычка. Дело в том, что с точки зрения Perl правая часть s/// - это строка в двойных кавычках. \1 в обычной строке в двойных кавычках означает control-A . Обычное для Unix значение \1 сохранено в s/// . Однако, если вы привыкните делать именно так, у вас будут проблемы при добавлении модификатора /e .

S/(\d+)/ \1 + 1 /eg; или если вы попытаетесь выполнить s/(\d+)/\1000/;

Этой двусмысленности нельзя избежать, написав \{1}000 , но можно, если написать ${1}000 . Просто операцию интерполяции не надо путать с операцией сопоставления с обратной ссылкой. Конечно, они имеют разное значение в левой части оператора s/// .

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

Метасимволы

Метасимволы делятся на две группы. Одни из них действуют внутри шаблонов вообще, а другие внутри символьных определений.
«()» обозначают начало и конец второстепенного шаблона.
«» — начало и конец символьных определений.
Обратный слеш перед некоторыми буквами означает их служебную функцию, например «\n» означает переход на новую строку, а «\» перед «+-\(){}» означает их реальное значение. ^ в начале символьного определения говорит о том, что выражение не должно содержать таких символов, поэтому шаблон «[^aeiouy]» значит что символ не является гласной.
Вне квадратных скобок те же символы имеют обратное значение: например, знак «\d» означает число, «\D» – все кроме чисел, знак «^» означает начало выражения, а знак $ — конец.
Выражение «{x,y}» , где x и y – числа(x + — то же самое, что и {1,}
* — {0,}
? – {0,1}
«.» означает любой символ, кроме перехода на новую строку. Так для выделения комментариев в C++ подойдет шаблон (/\*.*\*/). Ему соответствует строка, заключенная между «/*» и
«*/».

Альтернатива

Теперь о конкретных случаях. Например, полный номер телефона можно записать так «+7-095-1234567» или «8-095-1234567» не создавать же два шаблона. Для таких случаев и предусмотрены альтернативы, вводимые знаком «|». Итак, «((8|\+7)\-(\d{3})\-(\d{5,}))» и есть нужный шаблон. Поясню: сначала идет выбор между «+7» и «8», далее «-» и трехзначный код, опять «-» и 5 или более цифр.

Проверки(Assertions)

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

«qwe(?=rty)» означает, что после «qwe» следует «rty»
«qwe(?!rty)» означает, что после «qwe» не следует
«rty»

Шаблону «{3,10}(? А строка, соответствующая шаблону «{1,10}(?

Таким образом можно задать и проверку Интернет-адреса, используя шаблон:

((http|ftp)\://((.(?

Сначала идет выбор между строками «http» и «ftp», далее «://». Затем идет последовательность любых символов(не являющихся «/»), завершающаяся точкой и комбинацией из двух трех или четырех букв и цифр (опция (?i) означает, что буква может быть как строчной, так и прописной). Далее идут «/» и любые другие символы. Тоже можно сделать и без конструкции(?

С увеличением длины строки увеличивается и время ее проверки, так шаблон «((\d+)qwe)», примененный к строке «123456789asd» будет работать примерно так:
9 цифр + qwe – не подходит,
8 цифр + qwe – не подходит,
7 цифр + qwe – не подходит,
и так вплоть до 1 цифры. Если строка длиннее, то сервер может просто зависнуть. Для предотвращения такой траты ресурсов и были придуманы одноразовые шаблоны. Вводятся они выражением «>?». Если шаблон «((?>\d+)qwe)» дошел до числа 9 и не удовлетворен результатом, то он не возвращается в начало, а продолжает проверку.

Рекурсивные шаблоны

Вместо создания вложенных шаблонов можно ввести рекурсию, обозначаемую в регулярных выражениях знаком «(?R)». Например шаблон «(\(((?>[^()]+)|(?R))*\))», примененный к строке «(abcd(ef(ghi)» оставит строку «(ghi)». Разберемся:

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

Условные шаблоны

Условные шаблоны названы условными, так как они соответствуют какому-либо условию. Они вводятся конструкциями
(?(условие)выражениееслида) и (?(условие)выражениееслида|
выражениееслинет). Вернемся к нашим баранам…… нет, шаблонам. Телефонный номер можно вводить с кодом города и без, тогда используя шаблон»((?(?

Я рассказал лишь о ключевых элементах шаблонов, оставив позади некоторые тонкости, тем не менее, остается ясно, что шаблоны предоставляют огромный контроль над информацией. Любую последовательность символов можно описать используя шаблоны. Единственное, что мешает – это различие форматов. Дело в том, что шаблоны могут
соответствовать стандарту POSIX или PCRE (Perl Compatible Regular Expressions). Хотя особых различий в них нет, разные языки предоставляют свои функции для каждого стандарта, но это уже совсем другая история…

В этой главе:

    Основные понятия
    Образцы
    Еще об операции сопоставления
    Операция замены
    Функции 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 даст больше информации.


Top