Существует ли определенное поведение для того, как регулярные выражения должны обрабатывать захват вложенных круглых скобок? Более конкретно, можете ли вы разумно ожидать, что разные движки будут фиксировать внешние скобки в первой позиции и вложенные скобки в последующих позициях?
Рассмотрим следующий код PHP (с использованием регулярных выражений PCRE)
<?php
$test_string = 'I want to test sub patterns';
preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
print_r($matches);
?>
Array
(
[0] => I want to test sub patterns //entire pattern
[1] => I want to test //entire outer parenthesis
[2] => want //first inner
[3] => to //second inner
[4] => patterns //next parentheses set
)
Сначала захватывается все выражение в скобках (я хочу протестировать), а затем следом захватываются внутренние шаблоны в скобках («хочу» и «к»). Это имеет логический смысл, но я мог видеть не менее логичный случай для первого захвата дополнительных скобок, а ЗАТЕМ захват всего шаблона.
Итак, это определенное поведение в механизмах регулярных выражений "сначала захватить все", или оно будет зависеть от контекста шаблона и / или поведения механизма (PCRE отличается от C #, чем отличается от Java, чем и т.д.)?
Ответы:
Из perlrequick
Предостережение : исключение открывающей скобки для группы, не связанной с захватом (? =)
Обновить
Я не часто использую PCRE, так как обычно использую настоящий;), но документы PCRE показывают то же, что и Perl:
Если PCRE отходит от совместимости с регулярными выражениями Perl, возможно, следует переопределить аббревиатуру - «Perl Cognate Regular Expressions», «Perl Comparable Regular Expressions» или что-то в этом роде. Или просто избавьтесь от смысловых букв.
источник
Да, это все довольно хорошо определено для всех интересующих вас языков:
"Группы захвата нумеруются путем подсчета их открывающих скобок слева направо. ... Группа ноль всегда означает все выражение ".
"Захваты с использованием () нумеруются автоматически в зависимости от порядка открывающих скобок, начиная с единицы. Первый захват, элемент захвата с нулевым номером - это текст, совпадающий со всем шаблоном регулярного выражения. ")
"\ 0 или $ 0 относится к тексту, совпадающему со всем шаблоном. Открывающие скобки подсчитываются слева направо (начиная с 1), чтобы получить номер подшаблона захвата ". (То же самое было и с устаревшими функциями POSIX)
PCRE - http://www.pcre.org/pcre.txt
Чтобы добавить к сказанному Аланом М., найдите «Как pcre_exec () возвращает захваченные подстроки» и прочтите пятый абзац, который следует ниже:
$ 1, $ 2 и т. Д. Соответствуют группам захвата, как и следовало ожидать (т.е. по появлению открывающей скобки), однако $ 0 возвращает имя программы, а не вся строка запроса - чтобы получить вместо этого $ &.
Скорее всего, вы найдете аналогичные результаты для других языков (Python, Ruby и других).
Вы говорите, что столь же логично сначала перечислить внутренние группы захвата, и вы правы - это просто вопрос индексации при закрытии, а не при открытии скобок. (если я вас правильно понял). Однако это менее естественно (например, не следует соглашению о направлении чтения) и поэтому затрудняет (вероятно, незначительно) определение путем проверки, какая группа захвата будет с заданным индексом результата.
Также имеет смысл помещать всю строку соответствия в позицию 0 - в основном для согласованности. Это позволяет всей согласованной строке оставаться в том же индексе независимо от числа групп захвата от регулярного выражения до регулярного выражения и независимо от количества групп захвата, которые фактически соответствуют чему-либо (например, Java будет свертывать длину массива согласованных групп для каждого захвата group не соответствует какому-либо контенту (подумайте, например, о чем-то вроде "шаблона (. *)"). Вы всегда можете проверить capturing_group_results [capturing_group_results_length - 2], но это плохо переводится на языки Perl, которые динамически создают переменные ($ 1 , $ 2 и т. Д.) (Perl, конечно, плохой пример, поскольку он использует $ & для совпадающего выражения, но вы поняли идею :).
источник
Я знаю, что все разновидности регулярных выражений группируются в порядке появления открывающих скобок. То, что внешние группы нумеруются перед содержащимися в них подгруппами, - это просто естественный результат, а не явная политика.
Что интересно, так это с именованными группами . В большинстве случаев они следуют той же политике нумерации по относительному положению скобок - имя является просто псевдонимом для номера. Однако в регулярных выражениях .NET именованные группы нумеруются отдельно от нумерованных групп. Например:
Regex.Replace(@"one two three four", @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)", @"$1 $2 $3 $4") // result: "two four one three"
Фактически, номер является псевдонимом для имени ; номера, присвоенные именованным группам, начинаются там, где заканчиваются «настоящие» пронумерованные группы. Это может показаться странной политикой, но для этого есть веская причина: в регулярных выражениях .NET вы можете использовать одно и то же имя группы более одного раза в регулярном выражении. Это делает возможным использование регулярных выражений, подобных тому из этого потока, для сопоставления чисел с плавающей запятой из разных языков:
^[+-]?[0-9]{1,3} (?: (?:(?<thousand>\,)[0-9]{3})* (?:(?<decimal>\.)[0-9]{2})? | (?:(?<thousand>\.)[0-9]{3})* (?:(?<decimal>\,)[0-9]{2})? | [0-9]* (?:(?<decimal>[\.\,])[0-9]{2})? )$
Если есть разделитель тысяч, он будет сохранен в группе «тысяча» независимо от того, какая часть регулярного выражения ему соответствует. Точно так же десятичный разделитель (если он есть) всегда будет сохранен в группе «десятичный». Конечно, есть способы идентифицировать и извлекать разделители без многоразовых именованных групп, но этот способ намного удобнее, я думаю, что он более чем оправдывает странную схему нумерации.
А еще есть Perl 5.10+, который дает нам больше контроля над захватом групп, чем я знаю, что делать. : D
источник
Порядок захвата в порядке левого парена является стандартным для всех платформ, на которых я работал. (Perl, php, ruby, egrep)
источник