2009-08-21 13 views
54

Czy istnieje zdefiniowane zachowanie dotyczące sposobu, w jaki wyrazy regularne powinny obsługiwać zachowanie przechwytujące zagnieżdżonych nawiasów? Dokładniej mówiąc, czy można rozsądnie oczekiwać, że różne silniki przechwycą zewnętrzne nawiasy w pierwszej pozycji i zagnieżdżą nawiasy w kolejnych pozycjach?Jak zagnieżdżone przechwytywanie grup ponumerowane w wyrażeniach regularnych?

Rozważmy następujący kod PHP (przy użyciu wyrażeń regularnych 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 
) 

Cały nawiasach wyrażenie jest zrobione pierwsze (Chcę, aby sprawdzić), a następnie wewnętrzne wzorce nawiasach są ujęte w przyszłym („chcę” i "do"). To ma sens logiczny, ale widziałem równie logiczny przypadek, w którym najpierw przechwycono podrzędne nawiasy, a następnie przechwycono cały wzorzec.

Czy jest to "zdefiniowane w pierwszej kolejności zachowanie zdefiniowane w silnikach o regularnych wyrażeniach, czy zależy to od kontekstu wzoru i/lub zachowania silnika (PCRE różni się od C# niż Java jest inna niż itp.)?

+0

Jeśli naprawdę interesują Cię wszystkie smaki regex, "agnostyk językowy" "Znacznik jest tym, czego chcesz." Istnieje zbyt wiele smaków, aby je wszystkie wymienić, a większość z nich nie jest zgodna z żadnym prawdziwym standardem (al. chociaż są niezwykle spójne, jeśli chodzi o numerowanie grup przechwytujących). –

+0

Dostęp do grupy można uzyskać za pomocą 1 $, 2 $, 3 $ .... itd. Jak uzyskać dostęp do 10. grupy? Czy będzie to 10 $? Nie sądzę, że 10 USD będzie działać, ponieważ będzie interpretowane jako 1 $, a następnie 0. Czy to oznacza, że ​​możemy mieć tylko 9 grup? Jeśli autor może, proszę, załączyć to jako część pytania, to będzie to jedyne miejsce, w którym można będzie dowiedzieć się wszystkiego o zagnieżdżonych grupach w wyrażeniach regularnych. – LionHeart

Odpowiedz

44

Od perlrequick

Jeśli grupy w wyrażeniach regularnych są zagnieżdżone, $ 1 otrzymuje grupę z otworem nawiasach skrajnym lewym, $ 2 następny otwór nawias itp

Aktualizacja

nie używam PCRE dużo, bo zazwyczaj używają prawdziwych), ale PCRE's docs wykazują takie same jak Perl:

podwzorów

2. It ustawia podstrumie jako wychwytujący podstrumień. Oznacza to, że gdy cały wzór się zgadza, ta część tematu, która jest zgodna z wzorcem podrzędnym, jest przekazywana z powrotem do osoby wywołującej za pomocą argumentu ovector o numerze . Otwarte nawiasy są liczone od lewej do prawej (zaczynając od 1), aby uzyskać liczbę wychwytujących wzorców.

Na przykład, jeśli ciąg „Czerwonego Króla” jest dopasowany do wzorca

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

przechwyconych podciągów są „czerwony król”, „czerwony” i „król” i są ponumerowane 1, 2 i 3, odpowiednio.

Jeśli PCRE odsuwa się od zgodności z wyrażeniem regularnym Perla, być może akronim powinien zostać przedefiniowany - "Perl Cognate Regular Expressions", "Perl Comparable Regular Expressions" lub coś takiego. Lub po prostu pozbyć się liter sensu.

+0

+1, ale zwróć uwagę, że nie używa on języka Perl. –

+1

@Sinan: używa PCRE w PHP, który jest "Perl-Compatible Regular Expressions"; więc powinno to być całkiem takie samo, jak użycie Perla bezpośrednio pod nazwą –

+2

Pascal, PCRE rozpoczęło próbę bycia zestawem zgodnym z Perl Compatible Regular Expression, ale w ostatnich latach te dwa elementy nieco się rozdzieliły. Nadal są bardzo podobne, ale istnieją subtelne różnice w zaawansowanych zestawach funkcji. (Także, na pytanie, jestem zainteresowany wszystkimi platformami) –

4

Kolejność rejestrowania w kolejności lewej paren jest standardem we wszystkich platformach, z którymi pracowałem w. (Perl, PHP, Ruby, egrep)

+0

"przechwytywanie w kolejności lewej strony" Dzięki temu jest to znacznie bardziej zwięzły sposób opisu zachowania. –

+2

Na jakich platformach pracowałeś? –

+1

Możesz ponownie przeliczyć zdjęcia w Perlu 5.10 i Perlu 6. –

8

Każdy smak regex, jaki znam, grupuje według kolejności, w jakiej pojawiają się nawiasy otwierające. Te grupy zewnętrzne są ponumerowane, zanim zawarte w nich podgrupy są naturalnym wynikiem, a nie jawną polityką.

Tam, gdzie jest interesujący, jest o nazwie grupy. W większości przypadków stosują tę samą politykę numerowania według względnych pozycji parens - nazwa jest jedynie aliasem dla liczby. Jednak w wyrażeniach .NET wymienione grupy są ponumerowane niezależnie od ponumerowanych grup. Na przykład:

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

// result: "two four one three" 

W efekcie liczba jest aliasem nazwy; numery przypisane do nazwanych grup zaczynają się, gdy "rzeczywiste" ponumerowane grupy zostają wyłączone. Może się to wydawać dziwaczną polityką, ale jest ku temu dobry powód: w wyrażeniach .NET możesz używać tej samej nazwy grupy więcej niż raz w wyrażeniu regularnym. To sprawia, że ​​możliwe regexes jak ten z this thread dla dopasowania liczb zmiennoprzecinkowych z różnych lokalizacjach:

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

Jeśli istnieje tysiące separator, zostanie on zapisany w grupie „tysiącach” bez względu na to, która część regex dopasować go . Podobnie, separator dziesiętny (jeśli taki istnieje) będzie zawsze zapisywany w grupie "dziesiętny". Oczywiście są sposoby identyfikowania i wyodrębniania separatorów bez grup nazwanych do ponownego użycia, ale ten sposób jest o wiele wygodniejszy, myślę, że to więcej niż usprawiedliwia dziwny schemat numerowania.

A potem mamy Perla 5.10+, co daje nam większą kontrolę nad grupami przechwytującymi, niż wiem, co zrobić. : D

14

Tak, to wszystko jest prawie dobrze zdefiniowane dla wszystkich języków jesteś zainteresowany:

  • Java - http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    „grupy Przechwytywanie są numerowane poprzez zliczanie ich nawiasów otwierających od lewej w prawo ... Grupa zero zawsze oznacza całe wyrażenie. "
  • .Net - http://msdn.microsoft.com/en-us/library/bs2twtah(VS.71).aspx
    „Przechwytuje using() są numerowane automatycznie w oparciu o postanowienia nawiasie otwierającym, począwszy od jednego Pierwszy uchwycenia, element przechwytywania numer zero, to tekst dopasowany przez całe wyrażenie regularne. wzór „)
  • funkcje PHP (PCRE) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    .”. \ 0 i $ 0 odnosi się do tekstu dopasowane do całego wzoru nawiasy otwarcia liczy się od lewej do prawej (począwszy od dnia 1), aby uzyskać numer z przechwytywania subpattern. " (To samo dotyczy przestarzałych funkcji POSIX)
  • PCRE - http://www.pcre.org/pcre.txt
    Aby dodać do tego, co Alan M powiedział, szukać „jak pcre_exec() zwraca przechwyconych podciągów” i przeczytać akapit piąty, który następuje:

     
    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 inny - http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1, $ 2 itd mecz przechwytywanie grup jak można się spodziewać (czyli o wystąpieniu otwierającym uchwytem), jednak 0 $ zwraca nazwę programu, a nie cały ciąg kwerendy - do weź nas, ty e $ & zamiast tego.

Najprawdopodobniej znajdziesz podobne wyniki dla innych języków (Python, Ruby i inne).

Mówisz, że równie logiczne jest najpierw wypunktowanie wewnętrznych grup przechwytujących i masz rację - to tylko kwestia indeksowania zamknięcia, a nie otwierania, parens. (jeśli dobrze cię rozumiem). Takie postępowanie jest jednak mniej naturalne (na przykład nie jest zgodne z konwencją kierunku czytania), a zatem utrudnia (prawdopodobnie nie znacząco) określenie, według oceny, która grupa przechwytująca znajdzie się na danym wskaźniku wyników.

Umieszczenie całego ciągu meczowego w pozycji 0 ma również sens - głównie dla spójności. Pozwala to, aby cały dopasowany ciąg pozostawał w tym samym indeksie, niezależnie od liczby grup przechwytujących od wyrażenia regularnego, do regex i niezależnie od liczby grup przechwytujących, które faktycznie pasują do niczego (Java na przykład zwinie długość dopasowanej tablicy grup dla każdego przechwytywania grupa nie pasuje do żadnej treści (na przykład coś w stylu "wzorzec (. *)". Zawsze możesz sprawdzić wyniki grupy przechwytywania [capture_group_results_length - 2], ale to nie przekłada się dobrze na języki do Perla, które dynamicznie tworzą zmienne (1 $ , $ 2 itd.) (Perl jest złym przykładem, ponieważ używa $ & dla dopasowanego wyrażenia, ale masz pomysł :).

Powiązane problemy