Perl — один из языков, где регулярные выражения интегрированы на глубоком уровне. Главный оператор замены — s///, который позволяет искать и заменять фрагменты строк по шаблону. В этой конструкции кроется множество тонких моментов, особенно при работе с жадными/ленивыми шаблонами, многострочной обработкой и опциями замены.
Оператор s/// присутствует в Perl с ранних версий, и именно Perl заложил основы синтаксиса регулярных выражений, позже заимствованные другими языками. Большинство нюансов построения шаблонов и модификаторов (g, m, s, i, x и др.) появились и получили развитие именно в Perl.
На практике многие разработчики неправильно используют жадные квантификаторы или путаются в модификаторах (особенно s и m), что приводит к неожиданным результатам при замене во многострочных текстах или больших данных. Ошибки случаются при ожидании одного совпадения в строке, а получение другого, либо при замене только первой/последних вхождений.
Важно правильно выбирать и настраивать шаблоны и понимать действие модификаторов. Жадные шаблоны (например, .*) захватывают максимальный возможный диапазон, а ленивые (например, .*?) — минимально необходимый.
Работа с модификаторами:
g — выполняет замену для всех совпаденийs — включает режим обработки многострочных строк, где точка (.) захватывает символ перевода строкиm — меняет поведение маркеров ^ и $Пример — заменить теги <tag> ... </tag> на пробел только по одному тегу за раз (ленивый):
my $text = 'a <tag>1</tag> <tag>2</tag> b'; $text =~ s/<tag>.*?<\/tag>//g; print $text; # a b
Для обработки многострочных строк:
my $data = "Line 1 Line 2 <tag> DATA </tag> End"; $data =~ s/<tag>.*?<\/tag>//gs; print $data;
Ключевые особенности:
s позволяет точке (.) захватывать переводы строкg влияет на количество произведённых заменЧто произойдет, если не указать ? после жадного . при обработке нескольких тегов?*
Жадный квантификатор захватит максимально возможный диапазон, включая промежуточные теги, что приведёт к неожиданному удалению всех между первым <tag> и последним </tag>:
my $txt = 'A <tag>1</tag> <tag>2</tag> B'; $txt =~ s/<tag>.*<\/tag>//g; print $txt; # A B
Здесь заменён весь кусок между первым <tag> и последним </tag>.
Чем отличается модификатор m от модификатора s в регулярных выражениях Perl?
s — точка (.) захватывает символ новой строки; m — меняет якори ^ и $ на работу в пределах строк внутри многострочного текста. Их назначение различное, но часто их путают.
my $s = "abc def"; # /^def/ не сработает без m print $s =~ /^def/m; # 1 (true)
Как обработать все вхождения паттерна, если применить s/// только один раз?
Без модификатора g будет заменено только первое вхождение. Нужно добавлять g для глобальной замены:
my $s = "foo bar foo"; $s =~ s/foo/baz/g; # заменит оба foo
Разработчик пишет замену для HTML-тегов так:
$text =~ s/<tag>.*<\/tag>//g;
В результате из текста вырезаются все теги вместе с содержимым между ними — а не каждый по отдельности.
Плюсы:
Минусы:
Использовать ленивый шаблон и верные модификаторы:
$text =~ s/<tag>.*?<\/tag>//gs;
Плюсы:
Минусы: