Perl to jeden z języków, w których wyrażenia regularne są zintegrowane na głębokim poziomie. Główny operator zamiany — s///, który pozwala na wyszukiwanie i zamienianie fragmentów ciągów według wzorca. W tej konstrukcji kryje się wiele subtelnych kwestii, szczególnie przy pracy z zachłannymi/leniwe wzorcami, wieloliniowym przetwarzaniem i opcjami zamiany.
Operator s/// jest obecny w Perlu od wczesnych wersji, i to właśnie Perl położył fundamenty składni wyrażeń regularnych, które później zostały przejęte przez inne języki. Większość niuansów dotyczących budowania wzorców i modyfikatorów (g, m, s, i, x itd.) pojawiło się i rozwinęło właśnie w Perlu.
W praktyce wielu deweloperów niewłaściwie używa zachłannych kwantyfikatorów lub myli się w modyfikatorach (szczególnie s i m), co prowadzi do nieprzewidzianych rezultatów przy zamianie w wieloliniowych tekstach lub dużych zbiorach danych. Błędy pojawiają się przy oczekiwaniach na jedno dopasowanie w ciągu, a uzyskiwaniu innego, lub przy zamianie tylko pierwszego/ostatnich wystąpień.
Ważne jest, aby poprawnie wybierać i konfigurować wzorce oraz rozumieć działanie modyfikatorów. Zachłanne wzorce (np. .*) przechwytują maksymalny możliwy zakres, a leniwe (np. .*?) — minimalnie potrzebny.
Praca z modyfikatorami:
g — wykonuje zamianę dla wszystkich dopasowańs — włącza tryb przetwarzania wieloliniowych ciągów, gdzie kropka (.) przechwytuje znak nowej liniim — zmienia zachowanie znaczników ^ i $Przykład — zamienić znaczniki <tag> ... </tag> na spację tylko po jednym znaczniku na raz (leniwy):
my $text = 'a <tag>1</tag> <tag>2</tag> b'; $text =~ s/<tag>.*?<\/tag>//g; print $text; # a b
Do przetwarzania wieloliniowych ciągów:
my $data = "Line 1 Line 2 <tag> DATA </tag> End"; $data =~ s/<tag>.*?<\/tag>//gs; print $data;
Kluczowe cechy:
s pozwala kropce (.) przechwytywać znaki nowej liniig wpływa na liczbę przeprowadzonych zamianCo się stanie, jeśli nie podasz ? po zachłannym . przy przetwarzaniu wielu znaczników?*
Zachłanny kwantyfikator przechwyci maksymalny możliwy zakres, w tym pośrednie znaczniki, co doprowadzi do nieprzewidzianego usunięcia wszystkich między pierwszym <tag> a ostatnim </tag>:
my $txt = 'A <tag>1</tag> <tag>2</tag> B'; $txt =~ s/<tag>.*<\/tag>//g; print $txt; # A B
Tutaj została zamieniona cała część między pierwszym <tag> a ostatnim </tag>.
Czym różni się modyfikator m od modyfikatora s w wyrażeniach regularnych Perl?
s — kropka (.) przechwytuje znak nowej linii; m — zmienia kotwice ^ i $ na działanie w obrębie linii w wieloliniowym tekście. Ich przeznaczenie jest różne, ale często są mylone.
my $s = "abc def"; # /^def/ nie zadziała bez m print $s =~ /^def/m; # 1 (prawda)
Jak przetworzyć wszystkie wystąpienia wzorca, jeśli zastosujesz s/// tylko raz?
Bez modyfikatora g zostanie zamienione tylko pierwsze wystąpienie. Należy dodać g dla globalnej zamiany:
my $s = "foo bar foo"; $s =~ s/foo/baz/g; # zamieni oba foo
Deweloper pisze zamianę dla znaczników HTML tak:
$text =~ s/<tag>.*<\/tag>//g;
W wyniku z tekstu usuwane są wszystkie znaczniki razem z zawartością między nimi — a nie każdy oddzielnie.
Zalety:
Wady:
Używanie leniwego wzorca i poprawnych modyfikatorów:
$text =~ s/<tag>.*?<\/tag>//gs;
Zalety:
Wady: