%+ и %-

| Нет комментариев

В Perl 5.10 появилась возможность давать имена сохраняемым совпадениям в регулярных выражениях. Чтобы эффективно пользоваться этим нововведением, необходимо разобраться в том, как работают хеши %+ и %-, куда попадают совпавшие фрагменты.

Именованные скобки в регулярных выражениях — это конструкция вида (?<name>...). Если сопоставление оказалось успешным, то к искомому фрагменту возможно обратиться через элементы хешей %+ и %-. Например, в этом примере совпадение будет доступно в переменной %+{name}.

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

Вот пример, где из строки 120 EUR in USD, please извлекают названия валют.

use v5.10;
use strict;
use Data::Dumper;

my $string = '120 EUR in USD, please';
my @currencies = $string =~ /(?<currencyName>[A-Z]{3})/g;
say Dumper(\@currencies);
say Dumper(\%+);
say Dumper(\%-);

Именованное выражение ?<currencyName> встречается один раз, но обрабатывается в цикле. В массиве @currencies окажется два элемента:

$VAR1 = [
          'EUR',
          'USD'
        ];

А в хешах %+ и %- будет лишь по одному:

$VAR1 = {
          'currencyName' => 'USD'
        };

$VAR1 = {
          'currencyName' => [
                              'USD'
                            ]
        };

Важно обратить внимание на то, что в эти хеши попала последняя совпавшая подстрока.

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

my $re = '[A-Z]{3}';
$string =~ /(?<from>$re).*(?<to>$re)/;
say Dumper(\%+);
say Dumper(\%-);

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

$VAR1 = {
          'to' => 'USD',
          'from' => 'EUR'
        };

$VAR1 = {
          'to' => [
                    'USD'
                  ],
          'from' => [
                      'EUR'
                    ]
        };

Обратить внимание, кстати, стоит на то, что в хеше %- поименованные элементы всегда содержат списки, даже если они состоят из одного элемента.

Наличие списков, однако, помогает, когда одноименных элементов становится больше:

$string =~ /(?<currencyName>$re).*(?<currencyName>$re)/;
say Dumper(\%+);
say Dumper(\%-);

В этом примере хеш %+ вновь содержит один элемент — последнее совпадение, но в ключе $-{currencyName} теперь список элементов из двух строк:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };

Ничто не мешает вынести и само имя фрагмента в отдельную переменную:

my $named = '?<currencyName>[A-Z]{3}';
$string =~ /($named).*($named)/;
say Dumper(\%+);
say Dumper(\%-);

Результат будет таким же, как и в предыдущем случае:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };
Важно помнить, что если именованное выражение было необязательным и не совпало, его следы все равно будут присутствовать в хеше %-:

$string = "120 EUR, please";
$string =~ /($named).*($named)?/;
say Dumper(\%-);

Этот пример вернет хеш со списком из строки и значения undef:

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              undef
                            ]
        };

Порядок элементов списка совпадает с порядком именованных скобок в регулярном выражении.

Комментировать

Страницы

  • img

Об этой записи

Сообщение опубликовано 01.09.2009 23:18. Автор — ash.

Предыдущая запись — Perl и Project Euler

Следующая запись — Импорт курсов валют

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.