W jaki sposób grupy nie przechwytujące, tj. Są (?:)
używane w wyrażeniach regularnych i do czego służą?
W jaki sposób grupy nie przechwytujące, tj. Są (?:)
używane w wyrażeniach regularnych i do czego służą?
Odpowiedzi:
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.
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.
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ą.
?:
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.
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"]
(?:)
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ć.
MOTYWACJA HISTORYCZNA:
Istnienie grup nie przechwytujących można wyjaśnić za pomocą nawiasów.
Rozważ wyrażenia, (a|b)c
a a|bc
ze 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
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, \1
a następnie \2
przypominamy 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.
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 \1
oznacza, ż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).
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ć is
mię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"]
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.
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>)
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 example@example.com
. 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.
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, http
gdy myślałem o tym http
i dwukropek :
oba nie zostaną zgłoszone, ponieważ znajdują się w grupie, która nie została przechwycona.
console.debug(parse_url_regex.exec(url));
Pomyślałem, że jeśli pierwsza grupa nie (?:([A-Za-z]+):)
jest grupą przechwytującą, to dlaczego zwraca http
cią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 http
nadal 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.
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 JavaScript
RegExp 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ą.
Myślę, że dałbym ci odpowiedź. Nie używaj zmiennych przechwytywania bez sprawdzenia, czy dopasowanie się powiodło.
Zmienne przechwytywania $1
itp. 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.
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.