Регулярные выражения
Страница 5. Обратные ссылки, Lookahead- и Lookbehind-условия


 

Обратные ссылки, Lookahead- и Lookbehind-условия

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

Мы уже говорили об одной из важнейших возможностей регулярных выражений – способность сохранения части соответствий для дальнейшего использования. Кстати, избежать этого можно с помощью использования '?:'.

Например,

$test = "Today is monday the 18th.";
$test =~ m/([0-9]+)th/

сохранит "18" в $1, а

$test = "Today is monday the 18th.";
$test =~ m/[0-9]+th/

ничего не станет сохранять – из-за отсутствия скобок.

$test = "Today is monday the 18th.";
$test =~ m/(?:[0-9]+)th/

также ничего не станет сохранять благодаря использованию оператора '?:'.

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

$test = "Today is monday the 18th.";
$test =~ s/ the ([0-9]+)th/, and the day is $1/

приведет к записи "Today is monday, and the day is 18." в переменную $test.

Можно ссылаться на подстроки, уже найденные данным запросом, используя \1, \2, ..., \9. Следующее регулярное выражение удалит повторяющиеся слова:

$test = "the house is is big";
$test =~ s/\b(\S+)\b(\s+\1\b)+/$1/

записывает "the house is big" в $test.

Lookahead- и Lookbehind-условия

Иногда нужно сказать "найдите вот это, но только если перед ним не стоит вот этого", или "найдите вот это, но только если за ним не стоит вот этого". Пока речь идет об одиночном символе, достаточно воспользоваться [^...].

В более сложном случае придется использовать так называемые lookahead-условия или lookbehind-условия. Не путайте Positive lookahead с оптимистичным взглядом в будущее. Всего есть четыре типа таких условий:

  • Положительное lookahead-условие '(?=re)'
    Соответствует, только если за ним следует регулярное выражение re.
  • Отрицательное lookahead-условие '(?!re)'
    Соответствует, только если за ним не следует регулярное выражение re.
  • Положительное lookbehind-условие '(?<=re)'
    Соответствует, только если перед ним следует регулярное выражение re.
  • Отрицательное lookbehind-условие '(?<!re)'
    Соответствует, только если перед ним не следует регулярное выражение re.

Примеры:

$test = "HTML is a document description-language and not a programming-language";
$test =~ m/(?<=description-)language/

Найдет первое "language" ("description-language"), как предваряемое "description-", а

$test = "HTML is a document description-language and not a programming-language";
$test =~ m/(?<!description-)language/

Найдет второе "language" ("programming-language").

Следующие примеры выполнены в .Net. Поиск осуществляется в следующем тексте:

void aaa
{
if(...)
{
try { ... }
catch(Exception e1) { MessageBox.Show(e1.ToString(), "Error"); }
finally { listBox1.EndUpdate(); }
}
}

Положительный Lookahead

Шаблон \{(?=[^\{]*\}).*?\} находит самый глубоко вложенный блок, выделенный фигурными скобками. Результат выполнения:

  1. { ... }
  2. { MessageBox.Show(e1.ToString(), "Error"); }
  3. { listBox1.EndUpdate(); }

Положительный Lookbehind

Шаблон (?<=try\s*)\{(?=[^\{]*\}).*?\} находит самый глубоко вложенный блок выделенный фигурными скобками, перед которым есть try. Результат выполнения: { ... }.

Отрицательный Lookbehind

Шаблон (?<!try\s*)\{(?=[^\{]*\}).*?\} находит самый глубоко вложенный блок выделенный фигурными скобками перед которым нет слова try. Результат выполнения:

  1. { MessageBox.Show(e1.ToString(), "Error"); }
  2. { listBox1.EndUpdate(); }

В этих примерах жирным выделены Lookahead- и Lookbehind-условия.

 
« Предыдущая статья   Следующая статья »