Jesteś tutaj: GłównaWyrażenia regularne → Grupowanie i odwołania wsteczne

Grupowanie i odwołania wsteczne

Wyobraź sobie, że chcesz znaleźć osoby, których numer PESEL zaczyna się tą samą cyfrą, którą się kończy – za pomocą wyrażeń regularnych, mądralo, a nie w jakimś języku programowania!

Można to zrobić, wypisując wszystkie możliwe kombinacje wyrażeń postaci cyfra\d{9}cyfra. Ale po? Prościej i łatwiej użyć grupowania i przechwytywania, prawda?

Czym to się je?

O grupowaniu już pisałem, warto jednak wspomnieć o nim jeszcze raz, w kontekście niniejszego rozdziału.

Grupowanie umożliwia wyodrębnienie pewnej części wyrażenia regularne, która przez mechanizm dopasowywania wzorców jest traktowana jako swoista całość; jak w przykładzie czwartym z rozdziału o powtórzeniach, następujący po zgrupowanym fragmencie wyrażenia znak plusa odnosił się do tej grupy właśnie, a nie bezpośrednio poprzedzającego go znaku. Podobnie Sty(czeń)? dopasuje napis Sty, jak i Styczeń (ale nie Stycz).

Przechwytywanie

To świetnie, prawda? Bardzo użyteczny przykład, który – póki co – w żaden sposób nie ułatwia wykonania opisanego we wstępie niniejszego rozdziału zadania. Niemniej, właśnie mechanizm grupowania (wraz z przechwytywaniem) pomoże je – te zadanie – rozwiązać.

Otóż wszystkie dopasowane fragmenty napisów, dopasowane do zgrupowanych części wyrażenia, zostają przeniesione do specjalnych zmiennych mechanizmu dopasowywania wzorców, do wartości których możesz się odwoływać (przechwycone napisy określa się również mianem odwołań wstecznych). Zmienne te oznaczane są za pomocą backslasha i cyfry \cyfra; przykład z początku rozdziału można „rozwiązać” używając wyrażenia (\d)\d{9}\1, co tłumaczy się jako: „znajdź napisy zawierające jedenastocyfrowe liczby, w których pierwsza cyfra jest taka sama, jak ostatnia”. Zwróć uwagę na odwołanie \1 - zawiera ono wartość dopasowaną przez zgrupowanie (\d); dzięki takiemu wzorcowi pierwsza cyfra faktycznie będzie taka sama, jak ostatnia!

A co jeśli nie chcę przechwytywać?

Mechanizm umożliwia przechwycenie maksymalnie dziewięciu napisów.

Już sam ten fakt może powodować pewne niedogodności; w praktyce bowiem dość często natknąć się można na wyrażenia regularne, które zawierają o wiele więcej niż dziewięć czy nawet dwadzieścia grup. Odwołanie się do jedenastego przechwyconego napisu jest – przy użyciu „tradycjnych” metod – niemożliwe.

Szczęśliwie mądrzy ludzie[1] udostępnili możliwość wyłączenia przechwytywania; za pomocą specjalnej sekwencji znaków możemy je – to przechwytywanie – wyłączyć, oszczędzając numerację zmiennych \liczba na naprawdę ważne fragmenty tekstu.

W tym celu zamiast zwyczajnych nawiasów (fragment wzorca) należy użyć sekwencji (?:fragment wzorca).

I tak wyrażenie (?:Numer)?(\d)\1 znajdzie w przeszukiwanym fragmencie tekstu wszystkie liczby dwucyfrowe, złożone z tych samych cyfr, którą mogą być – ale nie muszą – poprzedzone napisem Numer (czyli, przykładowo, Numer11 lub 00, ale nie Numero22, czy też 93). Nie wykorzystując „rozszerzonego zestawu sekwencji” napisalibyśmy (Numer)?(\d)\2, co w tym przykładzie wielce ograniczające nie jest, niemniej, jak napisałem wcześniej, może się złożyć, że poziom komplikacji wyrażenia regularnego będzie wymagał od nas oszczędnego korzystania z dostępnych zmiennych przechwycających.

Kilka szczegółów

Zadanie dla czytelnika.

Co będzie w \1, co w \2, a co w \3 po przepuszczeniu przez wzorzec ((\w+) (\w+)) tekstu Czerwony Kapturek?

Czy jeśli podpowiem, że w zagnieżdżonych grupach, kolejne grupy liczone są względem lewego nawiasu, to będzie ci łatwiej?

Bo to proste jest, istotnie! W \1 znajdzie się tekst Czerwony Kapturek. I dalej nie ma już nic nadzwyczajnego – do \2 trafi Czerwony, a do \3 Kapturek.

I kolejne zadanie: czym różnią się wyrażenia (\d)+ od (\d+)?

W przypadku pierwszym, dopasowana zostanie cyfra – jeden lub więcej razy – a następnie zachowana w zmiennej \1; w przypadku drugim dopasowana zostanie jedno- lub więcejcyfrowa liczba, która zostanie zapamiętana w zmiennej \1.

Przypisy

[1] Twórcy perla