Регулярные выражения Perl. Регулярные выражения (шаблоны)

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() , например.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Поиск чисел

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Eaeaeeeeee eeaeaee

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

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

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

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

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

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

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

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/// .

> Регулярные выражения в 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.