Perl не жадная проблема - PullRequest
4 голосов
/ 14 мая 2011

У меня проблема с не жадным регулярным выражением.Я видел, что есть вопросы, касающиеся не жадных регулярных выражений, но они не отвечают на мою проблему.

Проблема: Я пытаюсь соответствовать href "lol"якорь.

Примечание: Я знаю, что это можно сделать с помощью модулей синтаксического анализа Perl HTML, и мой вопрос не о синтаксическом анализе HTML в Perl.У меня вопрос о самом регулярном выражении, а HTML - только пример.

Тестовый пример: У меня есть 4 теста для .*? и [^"].Первые 2 дают ожидаемый результат.Однако третий не делает, а четвертый просто делает, но я не понимаю, почему.

Вопросы:

  1. Почему делает3-й тест не пройден в обоих тестах для .*? и [^"]?Должен ли работать не жадный оператор?
  2. Почему работает 4-й тест в обоих тестах для .*? и [^"]?Я не понимаю, почему включение .* в начале меняет регулярное выражение.(3-й и 4-й тесты одинаковы, за исключением .* спереди).

Я, вероятно, не совсем понимаю, как работают эти регулярные выражения. рецепт рецепта Perl упоминает что-то, но я не думаю, что он отвечает на мой вопрос.

use strict;

my $content=<<EOF;
<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a href="/lol/lol/lol/lol/lol" class="lol">lol</a>
<a href="/koo/koo/koo/koo/koo" class="koo">koo</a>
EOF

print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)"~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)".*>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nWhy does not the 2nd non-greedy '?' work?\n"
  if $content =~ m~href="(.*?)".*?>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nIt now works if I put the '.*' in the front?\n"
  if $content =~ m~.*href="(.*?)".*?>lol~s ;

print "\n###################################################\n";
print "Let's try now with [^]";
print "\n###################################################\n\n";


print "| $1 | \n\nThat's ok\n" if $content =~ m~href="([^"]+?)"~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThat's ok.\n" if $content =~ m~href="([^"]+?)".*>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nThe 2nd greedy still doesn't work?\n"
  if $content =~ m~href="([^"]+?)".*?>lol~s ;

print "\n---------------------------------------------------\n";

print "| $1 | \n\nNow with the '.*' in front it does.\n"
  if $content =~ m~.*href="([^"]+?)".*?>lol~s ;

Ответы [ 4 ]

6 голосов
/ 14 мая 2011

Попробуйте распечатать $& (текст, соответствующий всему регулярному выражению), а также $1.Это может дать вам лучшее представление о том, что происходит.

Проблема, с которой вы, похоже, сталкиваетесь, заключается в том, что .*? не означает «Найти совпадение из всех возможных совпадений, использующих здесь меньшее количество символов».Это просто означает: «Сначала попробуйте сопоставить 0 символов здесь и перейдите к соответствию остальному регулярному выражению. Если это не удастся, попробуйте сопоставить 1 символ. Если остальные регулярные выражения не будут совпадать, попробуйте 2 символа здесь» и т. Д."

Perl всегда найдет совпадение, которое начинается ближе всего к началу строки .Поскольку большинство ваших паттернов начинаются с href=, он найдет первый href= в строке и посмотрит, есть ли способ расширить повторения, чтобы получить совпадение, начинающееся там.Если он не может найти совпадение, он попытается начать со следующего href= и т. Д.

Когда вы добавляете жадный .* в начало регулярного выражения, сопоставление начинается с.* захватывая как можно больше символов.Perl затем возвращается, чтобы найти href=.По сути, это заставляет его сначала попробовать last href= в строке и перейти к началу строки.

0 голосов
/ 14 мая 2011

Позвольте мне попытаться проиллюстрировать, что здесь происходит (см. Другие ответы, почему это происходит):

href="(.*?)"

Совпадение: href="/hoh/hoh/hoh/hoh/hoh" Группа: /hoh/hoh/hoh/hoh/hoh

href="(.*?)".*>lol

Совпадение: href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

Группа: /hoh/hoh/hoh/hoh/hoh

href="([^"]+?)".*?>lol

Совпадение: href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

Группа: /hoh/hoh/hoh/hoh/hoh

.*href="(.*?)".*?>lol

Совпадение: <a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

Группа: /lol/lol/lol/lol/lol

В одну сторону чтобы написать регулярное выражение, которое вы хотите использовать: href="[^"]*"[^>]*>lol

0 голосов
/ 14 мая 2011

Основная проблема в том, что вы используете не жадные регулярные выражения, когда вы не должны.Вторая проблема заключается в использовании.с *, который может случайно совпадать с тем, что вы намеревались.Флаг s, который вы используете make.еще больше соответствия.

Использование:

m~href="([^"]+)"[^>]*>lol~

для вашего случая.Что касается не жадных регулярных выражений, учтите, что код:

$_ = "xaaaaab xaaac xbbc";
m~^x.+?c~;

Он не будет соответствовать 'xaaac', как вы могли ожидать, он будет начинаться с начала строки и совпадать с 'xaaaaab xaaac'.Жадный вариант соответствовал бы всей строке.

Дело в том, что, хотя не жадные регулярные выражения не пытаются захватить столько, сколько они могут, они все же пытаются как-то соответствовать с той же готовностью, что и их жадные братья.,И они будут захватывать любую часть строки, чтобы сделать это.

Вы также можете рассмотреть «собственнический» квантификатор, который отключает возврат.Кроме того, поваренные книги хороши для начала, но если вы хотите понять, как все на самом деле работает, вы должны прочитать это - perlre

0 голосов
/ 14 мая 2011

Работает только 4-й контрольный пример.

первый m~href="(.*?)"~s Это будет соответствовать первому href в вашей строке и захватывать то, что находится между кавычками, так: /hoh/hoh/hoh/hoh/hoh

Второй: m~href="(.*?)".*>lol~s Это будет соответствовать первому href в вашей строке и захватывать то, что находится между кавычками, а затем совпадать с любым любым числом любого символа, пока не найдет >lol так: /hoh/hoh/hoh/hoh/hoh

Попробуйте захватить .* с помощью m~href="(.*?)"(.*)>lol~s

$1 contains :
/hoh/hoh/hoh/hoh/hoh
$2 contains : 
class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a href="/lol/lol/lol/lol/lol" class="lol" 

третий: m~href="(.*?)".*?>lol~s Тот же результат, что и в предыдущем тестовом примере.

Четвертый: m~.*href="(.*?)".*?>lol~s Это будет соответствовать любому числу любого символа, затем href=", затем захватить любое число любого символа, не жадного до кавычки, затем соответствовать любому любому числу любого символа, пока не найдет >lol так: /lol/lol/lol/lol/lol

Попробуйте захватить все .* с помощью m~(.*)href="(.*?)"(.*?)>lol~s

$1 contains :
<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a
$2 contains : 
/lol/lol/lol/lol/lol
$3 contains :
class="lol"

Посмотрите на этот сайт , он объясняет, что делают ваши регулярные выражения.

...