ПрограммированиеFullstack разработчик

Объясните принцип ленивой и жадной обработки квантификаторов в регулярных выражениях Perl. Как это влияет на парсинг строк? Приведите примеры тонких моментов и нестандартного поведения.

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Perl квантификаторы в регулярных выражениях — *, +, ?, {n,m} — по умолчанию жадные (greedy): они захватывают максимально возможное количество символов, соответствующее шаблону.

Добавление ? после квантификатора превращает его в ленивый (lazy или non-greedy): он захватывает минимально возможное количество символов, чтобы вся регулярка совпала.

Пример жадного совпадения:

my $str = 'foo <bar> baz <quux>'; $str =~ /<.*>/; # Захватит '<bar> baz <quux>'

Пример ленивого совпадения:

my $str = 'foo <bar> baz <quux>'; $str =~ /<.*?>/; # Захватит '<bar>'

Особенность:

Жадное выражение может «съесть» больше, чем ожидаете при парсинге HTML и других вложенных конструкций!


Вопрос с подвохом.

Чем отличаются следующие две регулярки при разборе строки <a><b><c>: /<(.*)>/ и /<(.*?)>/?

Ответ:

  • /<(.*)>/ (жадная) захватит максимальный блок — совпадение: <a><b><c>
  • /<(.*?)>/ (ленивая) — только первую группу: <a>

Пример:

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

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В приложении для импорта новостных заголовков программист хотел распарсить название тега в строке <title>Новость</title>, используя /\<(.*)\>/. В итоге регулярка захватывала всю строку между < первой и > последней, а не нужный элемент. Ошибку нашли, когда появились вложенные теги.


История

В логическом парсере для выделения quoted strings используемый шаблон /"(.*)"/ неожиданно захватывал всё между первой и последней кавычкой. В результате разметка разбивалась некорректно, пока не заменили шаблон на /"(.*?)"/.


История

В автоматическом парсере CSV с возможностью кавычек был неправильно написан шаблон на "жадность", из-за чего несколько колонок склеивались в одну. Отказ введённого парсера выявился только на больших данных — ленивая модификация шаблона решила проблему.