
Regex zachłanny kontra leniwy: dlaczego twój wzorzec łapie za dużo
Opublikowano 24 lip 2026
Piszesz <b>.*</b>, aby złapać zawartość pogrubionego znacznika, testujesz na <b>Hello</b>, działa idealnie. Potem uruchamiasz to na prawdziwym HTML z dwoma znacznikami pogrubienia w tej samej linii — <b>Hello</b> and <b>World</b> — i zamiast dwóch dopasowań dostajesz jedno gigantyczne dopasowanie obejmujące oba znaczniki. To nie błąd w twoim silniku regex; to domyślne zachowanie *, + i {n,m}, i ma naprawę o długości jednego znaku, gdy już wiesz, co się dzieje.
Dlaczego .* łapie więcej, niż się spodziewasz
Domyślnie kwantyfikatory w regex są zachłanne — .* nie oznacza „dopasuj jakieś znaki”, oznacza „dopasuj jak najwięcej znaków, a wycofaj się tylko wtedy, gdy reszta wzorca absolutnie tego wymaga”.
Prześledźmy <b>.*</b> wobec <b>Hello</b> and <b>World</b> krok po kroku:
<b>dopasowuje pierwszy<b>..*zaczyna od pochłonięcia całej reszty ciągu — wszystkiego aż do końca, łącznie z drugim<b>World</b>.- Silnik musi potem znaleźć
</b>, aby zakończyć dopasowanie, więc zaczyna wycofywać się od końca.*po jednym znaku naraz. - Pierwsze miejsce (skanując wstecz od końca), gdzie pasuje
</b>, to ostatnie</b>w ciągu — nie to pierwsze, na którym „logicznie” powinien się zatrzymać.
Więc dopasowanie ostatecznie obejmuje całe <b>Hello</b> and <b>World</b>, ponieważ zachłanne dopasowywanie zawsze najpierw próbuje najdłuższego możliwego ciągu i skraca go tylko tak bardzo, jak to konieczne, aby reszta wzorca się powiodła.
Naprawa: uczyń kwantyfikator leniwym za pomocą ?
Dodanie ? zaraz po kwantyfikatorze zmienia go z zachłannego na leniwy (nazywany też „non-greedy” lub „reluctant”): *?, +?, ??, {n,m}?.
Leniwy kwantyfikator robi coś odwrotnego: zaczyna od dopasowania jak najmniejszej liczby znaków, a rozszerza się tylko wtedy, gdy reszta wzorca jeszcze nie może się powieść.
<b>.*?</b> wobec tego samego ciągu:
<b>dopasowuje pierwszy<b>..*?zaczyna od dopasowania zera znaków.- Silnik sprawdza: czy
</b>pasuje właśnie tutaj? Nie (jesteśmy przy „Hello…”, jeszcze nie przy</b>) — więc rozszerza.*?o dokładnie jeden znak i sprawdza ponownie. - Powtarza się to znak po znaku, aż
.*?pochłonie dokładnieHello, w którym to momencie</b>pasuje natychmiast.
Wynik: <b>Hello</b> i <b>World</b> wracają jako dwa osobne dopasowania, czyli niemal zawsze to, czego naprawdę chciałeś, parsując struktury podobne do znaczników lub ograniczników.
Kiedy naprawdę chcesz zachłanności (to nie tylko „zła wartość domyślna”)
Zachłanność nie jest błędem w języku — jest poprawna dla innego, równie częstego przypadku: dopasowania najbardziej zewnętrznej granicy czegoś, a nie najmniejszej jednostki wewnątrz. Jeśli wyciągasz „wszystko między pierwszym { a ostatnim }” z bloku tekstu przypominającego JSON (powiedzmy, aby złapać cały obiekt niezależnie od zagnieżdżenia), zachłanność jest dokładnie tym, czego potrzeba, a leniwość zatrzymałaby się na pierwszym wewnętrznym }, dając obcięty, nieprawidłowy fragment.
Reguła kciuka: leniwość dla powtarzanych małych fragmentów ograniczonych (znaczniki, ciągi w cudzysłowach, elementy listy); zachłanność dla „złap cały zewnętrzny zakres”.
Szybka ściągawka
| Wzorzec | Zachowanie | Używaj, gdy |
|---|---|---|
.*, .+, {n,m} |
Zachłanny — dopasowuje najdłuższy możliwy | Chcesz najbardziej zewnętrzny zakres albo w ciągu jest tylko jedno dopasowanie |
.*?, .+?, {n,m}? |
Leniwy — dopasowuje najkrótszy możliwy | Masz wiele podobnych ograniczonych fragmentów i chcesz każdy osobno |
Jeśli nie masz pewności, co dany wzorzec faktycznie robi na twoich prawdziwych danych, a nie na testowym ciągu, uruchomienie go na rzeczywistym wielodopasowaniowym tekście w testerze regex z podświetlaniem dopasowań na żywo natychmiast to uwidacznia — zobaczysz jeden gigantyczny podświetlony blok dla zachłanności kontra kilka osobnych dla leniwości, co zwykle jest szybsze niż rozumowanie znak po znaku.