Почему мое не-жадное регулярное выражение Perl все еще слишком много соответствует?

скажем, у меня есть строка, содержащая следующую строку:

"$tom" said blah blah blash.  "$dick" said "blah blah blah". "$harry" said blah blah blah.

и я хочу, чтобы извлечь

"$dick" said "blah blah blah"

у меня есть следующий код:

my ($term) = /(".+?" said ".+?")/g;
print $term;

но это дает мне больше, чем мне нужно:

"$tom" said blah blah blash.  "$dick" said "blah blah blah"

Я попытался сгруппировать свой шаблон в целом, используя не захватывающие parens:

my ($term) = /((?:".+?" said ".+?"))/g;

но проблема не исчезла.

Я перечитал раздел Nongreedy Quantifiers обучения Perl, но это никуда меня не привело далеко.

Спасибо за любое руководство, которое вы можете щедро предложить:)

4 ответов


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

"$tom" said blah blah blash.

и подумайте: "о, Материал, следующий за "сказал", не цитируется, поэтому я пропущу это."Он думает:" Ну, материал после "сказал" не цитируется,таким образом, это должно быть частью нашей цитаты.- Итак!--3--> игр

"$tom" said blah blah blash.  "$dick"

что вы хотите "[^"]+". Это будет соответствовать двум кавычкам, заключающим все, что не является кавычкой. Поэтому окончательное решение:

("[^"]+" said "[^"]+")

к сожалению " является достаточно своеобразным персонажем, к которому нужно относиться осторожно. Использовать:

my ($term) = /("[^"]+?" said "[^"]+?")/g;

и он должен работать нормально (это не для меня...!). Т. е. явно соответствуют последовательности "nondoublequotes", а не последовательности произвольных символов.


другие упоминали, как это исправить.

Я отвечу, как вы можете отладить это: вы можете видеть, что происходит, используя больше захватов:

 bash$ cat story | perl -nle 'my ($term1, $term2, $term3) = /(".+?") (said) (".+?")/g ; 
      print "term1 = \"$term1\" term2 = \"$term2\" term3 = \"$term3\" \n"; '
 term1 = ""$tom" said blah blah blash.  "$dick"" term2 = "said" term3 = ""blah blah blah""

ваша проблема здесь в том, что есть два возможных совпадения для вашего regexp, тот, который вы хотите (более короткий) и тот, который выбирает regex engine. Механизм выбирает это конкретное совпадение, потому что он предпочитает совпадение, которое начинается раньше в строке и длиннее совпадения, которое начинается позже и короче. Другими словами, ранние матчи выигрывают более короткие.

чтобы решить эту проблему, вам нужно сделать ваше регулярное выражение более конкретным (например, сообщить движку, что $term не должен содержать любые цитаты. Это хорошая идея, чтобы сделать ваши regexes как можно более конкретными в любом случае.

для более подробной информации и gotchas относительно регулярных выражений, я рекомендую отличную книгу Джеффри Фридля:Использование Регулярных Выражений