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

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

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

/pattern/modifiers

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

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

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

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

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

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

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)
Несоответствие с заглядыванием назад без запоминания найденного соответствия. Например, выражение /(?<!\t)\w+/ соответствует слову, перед которым нет символа табуляции. Фрагмент, соответствующий заглядыванию назад, должен иметь фиксированную ширину.

6.4.6. Операции с регулярными выражениями

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

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

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

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

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

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

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

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

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

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

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

Если 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[A-Za-z0-9]+\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|;

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

Модификатор Описание
e Обрабатывать string как выражение PERLа.
g Глобальная подстановка, т. е. замена всех вхождений образца.
o Компилировать регулярное выражение только один раз.

Мы можем использовать вместо // любой символ, допустимый в q-операциях. Если pattern заключен в парные скобки, то string должен иметь собственную пару ограничителей, например s(foo)[bar] или s<foo>/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)[a-z] или tr<A-Z>/a-z/.

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

Модификатор Описание
c Заменять символы, не входящие в list1.
d Удалять символы, для которых нет замены.
s Удалять повторяющиеся символы при замене.
U Преобразовывать в/из кодировку UTF-8.
C Преобразовывать в/из однобайтовую кодировку.

Модификатор 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(), например:

eval "tr/$oldlist/$newlist/";