Czy istnieje zdefiniowane zachowanie dotyczące sposobu, w jaki wyrażenia regularne powinny obsługiwać zachowanie przechwytywania zagnieżdżonych nawiasów? Mówiąc dokładniej, czy można rozsądnie oczekiwać, że różne silniki będą przechwytywać nawiasy zewnętrzne na pierwszej pozycji i nawiasy zagnieżdżone w kolejnych pozycjach?
Rozważ następujący kod PHP (używając 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
)
Najpierw przechwytywane jest całe wyrażenie w nawiasach (chcę przetestować), a następnie przechwytywane są wewnętrzne wzorce ujęte w nawiasy („chcę” i „do”). Ma to logiczny sens, ale mogłem zobaczyć równie logiczny przypadek, w którym najpierw przechwytuje się nawiasy dodatkowe, a NASTĘPNIE przechwytuje cały wzór.
Czy to "najpierw przechwyć całą rzecz" zdefiniowane zachowanie w silnikach wyrażeń regularnych, czy będzie zależało od kontekstu wzorca i / lub zachowania silnika (PCRE różni się od C # jest inny niż Java jest inny niż itp.)?
Odpowiedzi:
Od perlrequick
Uwaga : wykluczanie nawiasów otwierających grupy bez przechwytywania (? =)
Aktualizacja
Nie używam dużo PCRE, ponieważ generalnie używam prawdziwego;), ale dokumenty PCRE pokazują to samo, co Perl:
Jeśli PCRE odchodzi od zgodności z wyrażeniami regularnymi w Perlu, być może akronim powinien zostać przedefiniowany - „Perl Cognate Regular Expressions”, „Perl Comparable Regular Expressions” lub coś takiego. Albo po prostu pozbądź się znaczących liter.
źródło
Tak, to wszystko jest dość dobrze zdefiniowane dla wszystkich języków, które Cię interesują:
"Grupy przechwytywania są numerowane, licząc nawiasy otwierające od lewej do prawej. ... Grupa zero zawsze oznacza całe wyrażenie. "
"Przechwytywania za pomocą () są numerowane automatycznie w oparciu o kolejność nawiasów otwierających, zaczynając od jedynki. Pierwszy przechwytywanie, przechwytywanie elementu o numerze zero, to tekst dopasowany przez cały wzorzec wyrażenia regularnego. ")
"\ 0 lub $ 0 odnosi się do tekstu dopasowanego przez cały wzorzec. Nawiasy otwierające są liczone od lewej do prawej (zaczynając od 1), aby uzyskać numer podrzędnego wzorca przechwytywania. " (Dotyczyło to również przestarzałych funkcji POSIX)
PCRE - http://www.pcre.org/pcre.txt
Aby dodać do tego, co powiedział Alan M, wyszukaj „Jak pcre_exec () zwraca przechwycone podciągi” i przeczytaj piąty akapit poniżej:
$ 1, $ 2 itd. Dopasowują grupy przechwytywania zgodnie z oczekiwaniami (tj. Przez występowanie nawiasu otwierającego), jednak $ 0 zwraca nazwę programu, a nie cały ciąg zapytania - zamiast tego używasz $ &.
Najprawdopodobniej znajdziesz podobne wyniki dla innych języków (Python, Ruby i inne).
Mówisz, że równie logiczne jest wymienienie najpierw wewnętrznych grup przechwytywania i masz rację - to po prostu kwestia indeksowania przy zamykaniu, a nie otwieraniu parens. (jeśli dobrze cię rozumiem). Robienie tego jest jednak mniej naturalne (na przykład nie jest zgodne z konwencją kierunku czytania), a więc utrudnia (prawdopodobnie nieznacznie) określenie, poprzez wnikliwość, która grupa przechwytująca będzie miała dany indeks wyników.
Umieszczenie całego ciągu dopasowującego na pozycji 0 również ma sens - głównie ze względu na spójność. Pozwala to całemu dopasowanemu ciągowi pozostawać w tym samym indeksie niezależnie od liczby grup przechwytywania od wyrażenia regularnego do wyrażenia regularnego i niezależnie od liczby grup przechwytywania, które faktycznie pasują do czegokolwiek (na przykład Java zwinie długość tablicy dopasowanych grup dla każdego przechwytywania group nie pasuje do żadnej treści (pomyśl na przykład coś takiego jak „wzorzec (. *)”). Zawsze możesz sprawdzić capturing_group_results [capturing_group_results_length - 2], ale to nie przekłada się dobrze na języki do Perla, który dynamicznie tworzy zmienne ($ 1 , $ 2 itd.) (Perl jest oczywiście złym przykładem, ponieważ używa $ & jako dopasowanego wyrażenia, ale masz pomysł :).
źródło
Każdy rodzaj wyrażenia regularnego, jaki znam, grupuje według kolejności, w jakiej pojawiają się nawiasy otwierające. To, że zewnętrzne grupy są ponumerowane przed zawartymi w nich podgrupami, jest po prostu naturalnym wynikiem, a nie wyraźną polityką.
Ciekawie robi się z nazwanymi grupami . W większości przypadków stosują tę samą politykę numeracji przez względne pozycje parenów - nazwa jest jedynie aliasem numeru. Jednak w wyrażeniach regularnych .NET nazwane grupy są numerowane oddzielnie od grup numerowanych. 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ę tam, gdzie kończą się grupy „rzeczywiste”. Może się to wydawać dziwną zasadą, ale jest ku temu dobry powód: w wyrażeniach regularnych .NET możesz użyć tej samej nazwy grupy więcej niż raz w wyrażeniu regularnym. To umożliwia stosowanie wyrażeń regularnych, takich jak ten z tego wątku, do dopasowywania liczb zmiennoprzecinkowych z różnych lokalizacji:
^[+-]?[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 separator tysięcy, zostanie on zapisany w grupie „tysiąc” niezależnie od tego, która część wyrażenia regularnego do niego pasuje. Podobnie separator dziesiętny (jeśli taki istnieje) będzie zawsze zapisywany w grupie „decimal”. Oczywiście istnieją sposoby na identyfikację i wyodrębnienie separatorów bez nazwanych grup wielokrotnego użytku, ale jest to o wiele wygodniejsze, myślę, że usprawiedliwia to dziwny schemat numeracji.
Jest jeszcze Perl 5.10+, który daje nam większą kontrolę nad grupami przechwytywania, niż wiem, co robić. :RE
źródło
Kolejność przechwytywania w kolejności lewej strony jest standardowa na wszystkich platformach, na których pracowałem. (Perl, php, ruby, egrep)
źródło