Co to jest grupa nie przechwytująca w wyrażeniach regularnych?

Odpowiedzi:

2323

Pozwól, że wyjaśnię to przykładem.

Rozważ następujący tekst:

http://stackoverflow.com/
/programming/tagged/regex

Teraz, jeśli zastosuję nad nim regex ...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... uzyskałbym następujący wynik:

Match "http://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "/programming/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

Ale nie dbam o protokół - chcę tylko hosta i ścieżkę do adresu URL. Tak więc zmieniam wyrażenie regularne, aby uwzględnić grupę nie przechwytującą (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

Teraz mój wynik wygląda następująco:

Match "http://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "/programming/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

Widzieć? Pierwsza grupa nie została schwytana. Analizator składni używa go do dopasowania tekstu, ale w wyniku końcowym ignoruje go później.


EDYTOWAĆ:

Zgodnie z życzeniem pozwól mi też wyjaśnić grupy.

Grupy służą wielu celom. Mogą pomóc ci wyodrębnić dokładne informacje z większego dopasowania (które można również nazwać), pozwalają przeszukać poprzednią dopasowaną grupę i mogą być użyte do zastąpienia. Spróbujmy kilku przykładów, prawda?

Wyobraź sobie, że masz jakiś XML lub HTML (pamiętaj, że regex może nie być najlepszym narzędziem do tego zadania , ale jest to dobry przykład). Chcesz przeanalizować tagi, abyś mógł zrobić coś takiego (dodałem spacje, aby ułatwić zrozumienie):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

Pierwszy regex ma nazwaną grupę (TAG), podczas gdy drugi używa wspólnej grupy. Oba wyrażenia regularne robią to samo: używają wartości z pierwszej grupy (nazwy znacznika), aby dopasować znacznik zamykający. Różnica polega na tym, że pierwszy używa nazwy, aby dopasować wartość, a drugi używa indeksu grupy (który zaczyna się od 1).

Spróbujmy teraz podstawić. Rozważ następujący tekst:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

Teraz użyjmy tego głupiego wyrażenia regularnego:

\b(\S)(\S)(\S)(\S*)\b

Ten wyrażenie regularne dopasowuje słowa zawierające co najmniej 3 znaki i używa grup do oddzielenia pierwszych trzech liter. Rezultat jest następujący:

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

Jeśli zastosujemy łańcuch podstawienia:

$1_$3$2_$4

... staramy się użyć pierwszej grupy, dodać podkreślenie, użyć trzeciej grupy, potem drugiej grupy, dodać kolejny podkreślenie, a następnie czwartej grupy. Powstały ciąg będzie podobny do poniższego.

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

Możesz także używać nazwanych grup do podstawienia, używając ${name}.

Aby pobawić się wyrażeniami regularnymi , polecam http://regex101.com/ , która zawiera sporo szczegółów na temat działania wyrażeń regularnych; oferuje również kilka silników regex do wyboru.

Ricardo Nolde
źródło
3
@ajsie: Tradycyjne (przechwytywanie) grupy są najbardziej przydatne, jeśli wykonujesz operację zastępowania wyników. Oto przykład, w którym biorę rozdzielone przecinkami nazwiska i imiona, a następnie odwracam ich kolejność (dzięki nazwanym grupom) ... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30
Steve Wortham
2
Nie, to nie to samo.
Ricardo Nolde,
4
Może również wskazać, że grupy nie przechwytujące są wyjątkowo przydatne, gdy wyrażenie regularne jest używane jako separatory rozdzielone: ​​„Alice and Bob” -split "\ s + (?: and | or) \ s +"
Jewgienij
7
Interesujące byłoby wyjaśnienie różnicy między grupami nie przechwytującymi (? :), a twierdzeniami lookahead i lookbehind (? =,?!). Właśnie zacząłem uczyć się o wyrażeniach regularnych, ale z tego, co rozumiem, grupy nie przechwytujące są używane do dopasowywania i „zwracania” tego, co pasują, ale ta „wartość zwracana” nie jest „przechowywana” do odwoływania się wstecz. Z drugiej strony twierdzenia dotyczące wyprzedzenia i spojrzenia nie są nie tylko „przechowywane”, ale również nie są częścią dopasowania, po prostu twierdzą, że coś pasuje, ale ich wartość „dopasowania” jest ignorowana, jeśli się nie mylę. (Czy z grubsza mam rację?)
Christian
5
[] jest zbiorem; [123] jednokrotnie dopasowuje dowolny znak w zestawie; [^ 123] raz pasuje do niczego NIE w zestawie; [^ / \ r \ n] + odpowiada jednemu lub większej liczbie znaków różniących się od /, \ r, \ n.
Ricardo Nolde
180

Możesz użyć grup przechwytywania, aby zorganizować i przeanalizować wyrażenie. Grupa, która nie zdobywa, ma pierwszą korzyść, ale nie ma narzutu drugiej. Nadal możesz powiedzieć, że grupa, która nie przechwytuje, jest na przykład opcjonalna.

Powiedzmy, że chcesz dopasować tekst numeryczny, ale niektóre liczby można zapisać jako 1., 2., 3., 4., ... Jeśli chcesz przechwycić część liczbową, ale nie sufiks (opcjonalny), możesz użyć grupy, która nie jest przechwytywana .

([0-9]+)(?:st|nd|rd|th)?

To będzie pasowało do liczb w postaci 1, 2, 3 ... lub w postaci 1, 2, 3, ... ale przechwyci tylko część numeryczną.

Bill jaszczurka
źródło
3
Zwięzłe i prawdopodobnie najlepsze wyjaśnienie tutaj.
NelsonGon
106

?: jest używany, gdy chcesz pogrupować wyrażenie, ale nie chcesz zapisać go jako dopasowanej / przechwyconej części łańcucha.

Przykładem może być coś pasującego do adresu IP:

/(?:\d{1,3}\.){3}\d{1,3}/

Zauważ, że nie obchodzi mnie zapisywanie pierwszych 3 oktetów, ale (?:...)grupowanie pozwala mi skrócić wyrażenie regularne bez ponoszenia kosztów przechwytywania i przechowywania dopasowania.

RC.
źródło
38

Sprawia, że ​​grupa nie jest przechwytywana, co oznacza, że ​​podciąg pasujący do tej grupy nie zostanie uwzględniony na liście przechwyceń. Przykład w rubinie ilustrujący różnicę:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
sepp2k
źródło
Dlaczego nie możemy po prostu użyć „abc” .match (/.(.)./). Przechwytuje tutaj?
PRASANNA SARAF,
@PRASANNASARAF Możesz oczywiście. Chodziło o to, aby pokazać, że (?:)nie produkuje przechwytywania, a nie zademonstrować przydatny przykład (?:). (?:)jest przydatny, gdy chcesz pogrupować podwyrażenie (powiedz, kiedy chcesz zastosować kwantyfikatory do nie-atomowego podwyrażenia lub jeśli chcesz ograniczyć zakres a |), ale nie chcesz niczego wychwytywać.
sepp2k
26

MOTYWACJA HISTORYCZNA:

Istnienie grup nie przechwytujących można wyjaśnić za pomocą nawiasów.

Rozważ wyrażenia, (a|b)ca a|bcze względu na pierwszeństwo konkatenacji |wyrażenia te reprezentują dwa różne języki ( {ac, bc}i {a, bc}odpowiednio).

Jednak nawiasy są również używane jako grupa pasująca (jak wyjaśniono w innych odpowiedziach ...).

Jeśli chcesz mieć nawias, ale nie przechwytujesz podwyrażenia, korzystasz z GRUP NIEDOJMUJĄCYCH. W przykładzie(?:a|b)c

użytkownik2369060
źródło
6
Zastanawiałem się dlaczego. Uważam, że „dlaczego” jest niezbędne do zapamiętania tych informacji.
JMI MADISON
22

Pozwól mi spróbować z przykładem:

Kod Regex: (?:animal)(?:=)(\w+)(,)\1\2

Szukana fraza:

Linia 1 - animal=cat,dog,cat,tiger,dog

Linia 2 - animal=cat,cat,dog,dog,tiger

Linia 3 - animal=dog,dog,cat,cat,tiger

(?:animal) -> Nie przechwycona grupa 1

(?:=)-> Nie przechwycona grupa 2

(\w+)-> Przechwycona grupa 1

(,)-> Captured Group 2

\1 -> wynik złapanej grupy 1, tj. w linii 1 jest kot, w linii 2 jest kot, w linii 3 jest pies.

\2 -> wynik przechwyconej grupy 2, tj. przecinek (,)

Tak więc w tym kodzie, podając, \1a następnie \2przypominamy lub powtarzamy wynik przechwyconych grup 1 i 2 odpowiednio później w kodzie.

Zgodnie z kolejnością kodu (?:animal)powinna być grupa 1 i (?:=)powinna być grupa 2 i trwa nadal.

ale podając ?:znak „sprawiamy, że grupa dopasowań nie jest przechwytywana (które nie liczą się w dopasowanej grupie, więc numer grupy zaczyna się od pierwszej przechwyconej grupy, a nie nie przechwyconej”), tak aby powtórzenie wyniku grupy dopasowań (?:animal)nie można wywołać później w kodzie.

Mam nadzieję, że to wyjaśnia użycie grupy nieprzechwyconej.

wprowadź opis zdjęcia tutaj

shekhar gehlot
źródło
14

Grupy, które przechwytują , możesz użyć później w wyrażeniu regularnym w celu dopasowania LUB możesz użyć ich w zastępczej części wyrażenia regularnego. Utworzenie grupy nie przechwytującej po prostu zwalnia tę grupę z użycia z jednego z tych powodów.

Grupy nie przechwytujące są świetne, jeśli próbujesz uchwycić wiele różnych rzeczy, a istnieją grupy, których nie chcesz przechwytywać.

To właściwie powód ich istnienia. Podczas gdy uczysz się o grupach, poznajesz grupy atomowe , robią dużo! Istnieją również grupy opisowe, ale są one nieco bardziej złożone i nie są tak często używane.

Przykład użycia później w wyrażeniu regularnym (odwołanie wsteczne):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [Znajduje znacznik xml (bez wsparcia ns)]

([A-Z][A-Z0-9]*) jest grupą przechwytującą (w tym przypadku jest to zmienna)

Później w wyrażeniu regularnym \1oznacza, że ​​będzie pasował tylko do tego samego tekstu, który był w pierwszej grupie ( ([A-Z][A-Z0-9]*)grupie) (w tym przypadku pasuje do znacznika końcowego).

Bob Fincheimer
źródło
czy możesz podać prosty przykład, w jaki sposób zostanie on później wykorzystany do dopasowania OR?
never_had_a_name
Mam na myśli, że możesz użyć, aby dopasować później lub możesz użyć go w zastępstwie. Zdanie lub w tym zdaniu miało na celu pokazanie, że grupa przechwytująca ma dwa zastosowania
Bob Fincheimer
9

Cóż, jestem programistą JavaScript i postaram się wyjaśnić jego znaczenie dla JavaScript.

Rozważ scenariusz, w którym chcesz dopasować, cat is animal kiedy chcesz dopasować kota i zwierzę, i oba powinny mieć ismiędzy nimi przerwę.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]
Gauraw
źródło
7

W skomplikowanych wyrażeniach regularnych może wystąpić sytuacja, w której chcesz użyć dużej liczby grup, z których niektóre służą do dopasowywania powtórzeń, a niektóre z nich służą do dostarczania referencji wstecznych. Domyślnie tekst pasujący do każdej grupy jest ładowany do tablicy odwołań wstecznych. Tam, gdzie mamy wiele grup i musimy mieć możliwość odwoływania się do niektórych z nich z tablicy odwołań wstecznych, możemy zastąpić to domyślne zachowanie, aby powiedzieć wyrażeniu regularnemu, że niektóre grupy istnieją tylko w celu obsługi powtarzania i nie muszą być przechwytywane i przechowywane w tablicy odwołań wstecznych.

Jack Peng
źródło
7

Nie mogę komentować najważniejszych odpowiedzi, aby powiedzieć: chciałbym dodać wyraźny punkt, który jest sugerowany tylko w najlepszych odpowiedziach:

Grupa non-przechwytywanie (?...) ma nie usuwać żadnych znaków z oryginalnego pełnego meczu, tylko to reorganizuje regex wizualnie do programatora.

Aby uzyskać dostęp do określonej części wyrażenia regularnego bez zdefiniowanych obcych znaków, zawsze będziesz musiał użyć .group(<index>)

Scott Anderson
źródło
2
Podałeś najważniejszą wskazówkę, której brakowało w pozostałych odpowiedziach. Wypróbowałem wszystkie zawarte w nich przykłady i wykorzystałem najładniejsze z nich, ponieważ nie uzyskałem pożądanego rezultatu. Tylko twój post pokazał mi, gdzie popełniłem błąd.
Seshadri R
Miło to słyszeć!
Scott Anderson,
6

tl; dr grupy nie przechwytujące, jak sama nazwa wskazuje, to fragmenty wyrażenia regularnego, których nie chcesz uwzględniać w dopasowaniu, i ?:jest to sposób na zdefiniowanie grupy jako nie przechwytującej.

Załóżmy, że masz adres e-mail [email protected]. Poniższe wyrażenie regularne utworzy dwie grupy , część id i część @ example.com. (\p{Alpha}*[a-z])(@example.com). Dla uproszczenia wyodrębniamy całą nazwę domeny wraz z @postacią.

Powiedzmy, że potrzebujesz tylko części identyfikatora adresu. To, co chcesz zrobić, to pobrać pierwszą grupę wyniku dopasowania, otoczoną ()wyrażeniem regularnym, a sposobem na to jest użycie składni grupy nieprzechwycącej, tj ?:. Tak więc wyrażenie regularne (\p{Alpha}*[a-z])(?:@example.com)zwróci tylko część id wiadomości e-mail.

6-pak dzieci
źródło
5

Jedną interesującą rzeczą, na którą się natknąłem, jest fakt, że możesz mieć grupę przechwytującą wewnątrz grupy, która nie jest przechwytująca. Spójrz na wyrażenie regularne dla pasujących adresów URL:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Wpisz ciąg adresu URL:

var url = "http://www.ora.com:80/goodparts?q#fragment";

Pierwsza grupa w moim wyrażeniu regularnym (?:([A-Za-z]+):)to grupa nie przechwytująca, która jest zgodna ze schematem protokołu i :znakiem dwukropka, tj. http:Ale kiedy działałam poniżej kodu, widziałam, że pierwszy indeks zwróconej tablicy zawierał łańcuch, httpgdy myślałem o tym httpi dwukropek :oba nie zostaną zgłoszone, ponieważ znajdują się w grupie, która nie została przechwycona.

console.debug(parse_url_regex.exec(url));

wprowadź opis zdjęcia tutaj

Pomyślałem, że jeśli pierwsza grupa nie (?:([A-Za-z]+):)jest grupą przechwytującą, to dlaczego zwraca httpciąg znaków w tablicy wyjściowej.

Więc jeśli zauważysz, że w grupie ([A-Za-z]+)nie przechwytywanej jest zagnieżdżona grupa. Ta zagnieżdżona grupa ([A-Za-z]+)jest grupą przechwytującą (która nie ma ?:na początku) w sobie w grupie nie przechwytującej (?:([A-Za-z]+):). Dlatego tekst jest httpnadal przechwytywany, ale :znak dwukropka, który znajduje się w grupie, która nie została przechwycona, ale poza grupą przechwytującą, nie jest raportowany w tablicy wyjściowej.

RBT
źródło
2

Otwórz narzędzia Google Chrome devTools, a następnie kartę Konsola: i wpisz:

"Peace".match(/(\w)(\w)(\w)/)

Uruchom go, a zobaczysz:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

Silnik JavaScriptRegExp przechwytuje trzy grupy, elementy o indeksach 1,2,3. Teraz użyj znaku nie przechwytywania, aby zobaczyć wynik.

"Peace".match(/(?:\w)(\w)(\w)/)

Wynik to:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

Jest to oczywiste, co nie jest grupą przechwytującą.

AmerllicA
źródło
2

Myślę, że dałbym ci odpowiedź. Nie używaj zmiennych przechwytywania bez sprawdzenia, czy dopasowanie się powiodło.

Zmienne przechwytywania $1itp. Są niepoprawne, chyba że dopasowanie się powiedzie, a także nie zostaną wyczyszczone.

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

W powyższym przykładzie, aby uniknąć przechwytywania bronto $1,(?:) używany.

Jeśli wzór jest dopasowany, to $1 jest przechwytywany jako następny zgrupowany wzór.

Wynik będzie następujący:

Fred wants a burger

Jest to przydatne, jeśli nie chcesz, aby mecze były zapisywane.

Harini
źródło
1

Jest to niezwykle proste, możemy zrozumieć na podstawie prostego przykładu daty, załóżmy, że data jest wymieniona na 1 stycznia 2019 r. Lub 2 maja 2019 r. Lub na inną datę i po prostu chcemy ją przekonwertować na dd / mm / rrrr format , nie potrzebowalibyśmy miesiąca w tym przypadku jest to styczeń lub luty, więc aby uchwycić część liczbową, ale nie (opcjonalny) sufiks, możesz użyć grupy nie przechwytującej.

więc wyrażenie regularne brzmiałoby

([0-9]+)(?:January|February)?

To takie proste.

Naved Ahmad
źródło