2009-08-21 4 views
54

Есть ли определенное поведение для того, как регулярные выражения должны обрабатывать поведение захвата вложенных круглых скобок? В частности, можете ли вы разумно ожидать, что разные двигатели будут захватывать внешние скобки в первой позиции и вложенные круглые скобки в последующих позициях?Как вложенные группы захвата нумеруются в регулярных выражениях?

Рассмотрим следующий 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 
) 

Все выражение в скобках захватывается первым (я хочу проверить), а затем внутренние Скобках модели захвачены рядом («хочу» и "в"). Это логично, но я мог видеть одинаково логичный случай для первого захвата вспомогательных круглых скобок, а THEN - захват всего шаблона.

Итак, это «фиксировать все в первую очередь» определенное поведение в механизмах регулярных выражений, или оно будет зависеть от контекста шаблона и/или поведения двигателя (PCRE отличается от C# чем Java отличается от других)?

+0

Если вы действительно заинтересованы во всех ароматах регулярных выражений, язык-агностик «тег - это то, что вы хотите. Существует слишком много вкусов, чтобы перечислить их все, и большинство из них не соответствуют ни одному реальному стандарту (al хотя они замечательно совместимы, когда речь идет о нумерации групп захвата). –

+0

Доступ к группе возможен с помощью $ 1, $ 2, $ 3 .... и т. Д. Как получить доступ к 10-й группе? Будет ли это 10 долларов? Я не думаю, что $ 10 будет работать, потому что это будет интерпретироваться как $ 1, а затем 0. Означает ли это, что мы можем иметь только max 9 групп? Если автор может, пожалуйста, включить это как часть вопроса, тогда это будет одно место, чтобы знать все о вложенных группах в регулярных выражениях. – LionHeart

ответ

44

От perlrequick

Если группировки в регулярном выражении являются вложенным, $ 1 получает группу с крайней левой открывающей скобкой, $ 2 следующего открытием скобки и т.д.

Update

Я не использую PCRE много, как я обычно использую реальные вещи;), но PCRE's docs показывают такой же, как в Perl:

Подмаски

2. It устанавливает подшаблон как подшаблон захвата. Это означает, что, когда весь шаблон совпадает, эта часть строки субъекта, которая соответствует подшаблону, возвращается обратно вызывающему абоненту через аргумент ovectorpcre_exec(). Открывающиеся круглые скобки подсчитываются слева направо (начиная с 1), чтобы получить число для подматрицы захвата.

Например, если строка «красный король» сравнивается с рисунком

the ((red|white) (king|queen)) 

захвачены подстроки «красный король», «красный», и «король» и пронумерованы 1, 2 и 3 соответственно.

Если PCRE отходит от совместимости регулярных выражений Perl, возможно, аббревиатура должна быть пересмотрена - «Perl Родственные регулярных выражений», «Perl Сопоставимые регулярных выражений» или что-то. Или просто лишите буквы смысла.

+0

+1, но обратите внимание, что он не использует Perl. –

+1

@ Синан: он использует PCRE в PHP, который является «Perl-совместимыми регулярными выражениями»; поэтому он должен быть совершенно таким же, как непосредственно с Perl –

+2

Pascal, PCRE начал как попытку установить Perl Compatible Regular Expression, но в последние годы они немного расходятся. Все еще очень похоже, но есть тонкие различия в расширенных наборах функций. (Также, на вопрос, меня интересуют все платформы) –

4

Порядок захвата в порядке левого Paren является стандартным для всех платформ я работал в. (Perl, PHP, Ruby, задать расширенный)

+0

«захват в порядке слева». Спасибо за это, это гораздо более краткий способ описания поведения. –

+2

На каких платформах вы работали? –

+1

Вы можете повторно записать записи в Perl 5.10 и Perl 6. –

8

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

Куда интересным является названные группы. В большинстве случаев они следуют той же политике нумерации по относительным позициям parens - это имя является просто псевдонимом для числа. Однако в регулярных выражениях .NET именованные группы нумеруются отдельно от пронумерованных групп. Например:

Regex.Replace(@"one two three four", 
       @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)", 
       @"$1 $2 $3 $4") 

// result: "two four one three" 

В сущности, номер является псевдонимом для имени; числа, назначенные именованным группам, начинаются там, где «реальные» нумерованные группы остаются в силе. Это может показаться странной политикой, но для этого есть веская причина: в регулярных выражениях .NET вы можете использовать одно и то же имя группы более одного раза в регулярном выражении. Это делает возможным регулярные выражения, как один из this thread для сопоставления чисел с плавающей точкой из разных районов:

^[+-]?[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

14

Да, это все довольно много хорошо определены для всех языков вы заинтересованы в:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    «Захват группы нумеруются путем подсчета их открывающей скобки слева вправо. ... Ноль группы всегда обозначает все выражение ».
  • .Net - http://msdn.microsoft.com/en-us/library/bs2twtah(VS.71).aspx
    «захватывает с использованием() пронумерованы автоматически на основании порядка открывающей скобки, начиная с одного Первым захватом, захват элемент с номером ноля, текст соответствует по всему регулярному выражению. рисунок «)
  • функции PHP (PCRE) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    ».. \ 0 или $ 0 ссылается на текст согласованного по всему шаблону Открытие скобки нумеруются слева направо (начиная с 1) для получения номера захвата подшаблона ". (Это также относится и к устаревшим функциям POSIX)
  • PCRE - http://www.pcre.org/pcre.txt
    Чтобы добавить к тому, что сказал Алан M, искать «Как pcre_exec() возвращает захваченные подстроки» и прочитать пятый пункт, который следует:

    разные
     
    The first pair of integers, ovector[0] and ovector[1], identify the 
    portion of the subject string matched by the entire pattern. The next 
    pair is used for the first capturing subpattern, and so on. The value 
    returned by pcre_exec() is one more than the highest numbered pair that 
    has been set. For example, if two substrings have been captured, the 
    returned value is 3. If there are no capturing subpatterns, the return 
    value from a successful match is 1, indicating that just the first pair 
    of offsets has been set. 
    
  • в Perl - http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1, $ 2 и т.д. матч захвата группы, как вы могли бы ожидать (т.е. появления открывающей скобки), однако $ 0 возвращает имя программы, а не всю строку запроса - в узнайте, что вы нас e $ & вместо этого.

Вы, скорее всего, найдете похожие результаты для других языков (Python, Ruby и других).

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

Ввод всей строки матча в положение 0 также имеет смысл - в основном для согласованности. Он позволяет всей согласованной строке оставаться в одном индексе независимо от группы захвата числа от регулярного выражения к регулярному выражению и независимо от количества групп захвата, которые фактически соответствуют чему-либо (например, Java скроет длину массива согласованных групп для каждого захвата группа не соответствует какому-либо контенту (например, подумайте, например, как «a (. *) pattern»). Вы всегда можете проверить capture_group_results [capturing_group_results_length - 2], но это плохо переводит языки на Perl, которые динамически создают переменные ($ 1 , $ 2 и т. Д.) (Например, Perl - плохой пример, так как он использует $ & для совпадающего выражения, но вы получаете идею :).

Смежные вопросы