programowanieProgramista Fullstack

Wyjaśnij zasadę leniwego i chciwego przetwarzania kwantyfikatorów w wyrażeniach regularnych Perl. Jak wpływa to na parsowanie ciągów? Podaj przykłady subtelnych momentów i nienormatywnego zachowania.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Perl kwantyfikatory w wyrażeniach regularnych – *, +, ?, {n,m} – domyślnie są chciwe (greedy): łapią maksymalną możliwą ilość znaków, które pasują do wzorca.

Dodanie ? po kwantyfikatorze przekształca go w leniwy (lazy lub non-greedy): łapie minimalną możliwą ilość znaków, aby całe wyrażenie regularne było zgodne.

Przykład chciwego dopasowania:

my $str = 'foo <bar> baz <quux>'; $str =~ /<.*>/; # Złapie '<bar> baz <quux>'

Przykład leniwego dopasowania:

my $str = 'foo <bar> baz <quux>'; $str =~ /<.*?>/; # Złapie '<bar>'

Cechy:

Chciwe wyrażenie może „zjeść” więcej niż się spodziewasz podczas parsowania HTML i innych zagnieżdżonych konstrukcji!


Pytanie z zaskoczeniem.

Czym różnią się poniższe dwa wyrażenia regularne podczas analizy ciągu <a><b><c>: /<(.*)>/ i /<(.*?)>/?

Odpowiedź:

  • /<(.*)>/ (chciwe) złapie maksymalny blok – dopasowanie: <a><b><c>
  • /<(.*?)>/ (leniwy) – tylko pierwsza grupa: <a>

Przykład:

my $s = '<a><b><c>'; $s =~ /<(.*)>/; # $1: 'a><b><c' $s =~ /<(.*?)>/; # $1: 'a'

Przykłady rzeczywistych błędów z powodu braku znajomości subtelności tematu.


Historia

W aplikacji do importowania nagłówków wiadomości programista chciał sparsować nazwę tagu w ciągu <title>Nowość</title>, używając /\<(.*)\>/. W rezultacie wyrażenie regularne łapało cały ciąg między < pierwszym a > ostatnim, a nie potrzebny element. Błąd został znaleziony, gdy pojawiły się zagnieżdżone tagi.


Historia

W parserze logicznym do wydobywania cytowanych ciągów użyty wzór /"(.*)"/ niespodziewanie łapał wszystko między pierwszym a ostatnim cudzysłowem. W rezultacie formatowanie było niepoprawnie łamane, dopóki nie zamieniono wzoru na /"(.*?)"/.


Historia

W automatycznym parserze CSV z możliwością cudzysłowów był źle napisany wzór na „chciwość”, przez co kilka kolumn łączyło się w jedną. Błąd wprowadzony przez parser ujawnił się dopiero na dużych danych – leniwa modyfikacja wzoru rozwiązała problem.