Почему мое не-жадное регулярное выражение 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 относительно регулярных выражений, я рекомендую отличную книгу Джеффри Фридля:Использование Регулярных Выражений