根据下面的代码,我想匹配第一个form
次出现。我发现负面预测?!
可能会用来实现这一点,但它不起作用。我的正则表达式出了什么问题?
#test
$test = "<form abc> foo </form> <form gg> bar </form>";
$test =~ m/<form[^>]*abc[^>]*>(?!.*form>.*)form>/s;
print $&;
答案 0 :(得分:7)
首先,在解释正则表达式之前:使用像HTML::TreeBuilder
这样的模块来创建文档树,然后从那里获取您的信息。使用正则表达式解析HTML太容易在现实世界中使用。
这是你的字符串:
"<form abc> foo </form> <form gg> bar </form>"
你的正则表达式(为了可读性而扩展了书面,与/x
标志一样):
<form [^>]* abc [^>]* > (?! .* form> .* ) form>
<form
锚点
[^>]*
会搜索多个非>
个字符。最初它匹配 abc
abc
与文字字符序列abc
匹配。但是因为正则表达式引擎目前看到>
它必须回溯,直到[^>]*
匹配
。
[^>]*
将不会匹配,因为引擎会看到>
>
与>
当表达式.* form .*
不匹配时,否定前瞻匹配。
.*
会占用所有字符,直到字符串结束。
form>
会导致引擎回溯,直到.*
与foo </form> <form gg> bar </
匹配。
.*
没有任何匹配,但没关系。
所以前瞻成功,但这是一个消极的前瞻,所以断言失败了。正则表达式的最后一部分甚至不会被执行。
.*
在我们的案例中消耗了太多的字符。这称为贪婪匹配。
非贪婪匹配使用?
作为.*?
跟踪<form [^>]* > .*? </form>
。此版本最初消耗零个字符,并首先检查模式的下一部分。如果这不起作用,它会迭代地消耗另一个字符,直到匹配为止。
>
在开始标记内,只允许使用非attr="val<u>e"
个字符。在标签之间,允许任何字符。我们进行非贪婪匹配,因此第一个结束标记匹配并结束正则表达式。
但是,这个解决方案有点问题。容忍的HTML解析器不会阻塞</form>
。我们会。此外,第一个<div>
是匹配的,如果我们有嵌套表单,这是不可取的。虽然在这个用例中没有问题,但在匹配Regexp::Grammars
等时,这个正则表达式完全没用。
Perl正则表达式非常强大,允许您声明递归语法。内置语法有点笨拙,但我建议$&
模块轻松完成。更好的是,只需使用已经存在的完全成熟的HTML Parser。
不鼓励使用$`
(以及$'
和m{ ( <form [^>]* > .*? </form> ) }
),因为它会使perl极其低效。这不会在一个小脚本中表现出来,但无论如何它的坏样式。而是将整个Regexp与parens一起包含在捕获匹配
$1
然后使用{{1}}。
perlretut
Tutorial可能是理解Perl正则表达式的一个很好的介绍。