如何将html标记与perl正则表达式匹配?

Mar*_*ról -2 regex perl match lookahead

鉴于下面的代码,我想匹配第一次form出现.我发现负面前瞻?!可能用于实现这一目标,但它不起作用.我的正则表达式有什么问题?

#test
$test = "<form abc> foo </form> <form gg> bar </form>";
$test =~ m/<form[^>]*abc[^>]*>(?!.*form>.*)form>/s;
print $&;
Run Code Online (Sandbox Code Playgroud)

amo*_*mon 7

首先,在解释正则表达式之前:使用类似于HTML::TreeBuilder创建文档树的模块,然后从那里获取您的信息.使用正则表达式解析HTML太容易在现实世界中使用.

你的正则表达式的问题

这是你的字符串:

"<form abc> foo </form> <form gg> bar </form>"
Run Code Online (Sandbox Code Playgroud)

和你的正则表达式(为了可读性而扩展而写,与/x标志一样):

<form [^>]* abc [^>]* > (?! .* form> .* ) form>
Run Code Online (Sandbox Code Playgroud)
  • <form 找到文字字符序列时的锚点

  • [^>]*搜索许多非>字符.最初匹配 abc

  • abc匹配文字字符序列abc.但是因为regexp引擎目前看到 >它必须回溯,直到[^>]*匹配 .

  • [^>]* 什么都不匹配,因为发动机看到了 >

  • > 匹配 >

  • 当表达式.* form .*不匹配时,否定前瞻匹配.

    • .*会消耗所有字符,直到字符串的结尾.

    • form>导致引擎回溯直到.*匹配foo </form> <form gg> bar </.

    • .*场比赛没有什么,但是这是好的.

所以前瞻成功,但它是一个负面的前瞻,所以断言失败了.正则表达式的最后一部分甚至不会被执行.

策略

.*在我们的案例中,消耗了太多的字符.这称为贪婪匹配.

非贪婪匹配写有一个尾随?.*?.此版本最初消耗零个字符,并首先检查模式的下一部分.如果这不起作用,它会迭代地消耗另一个字符,直到匹配为止.

一个更好的正则表达式

<form [^>]* > .*? </form>
Run Code Online (Sandbox Code Playgroud)

在开始标记内,仅>允许非字符.在标签之间,允许任何字符.我们进行非贪婪匹配,因此第一个结束标记匹配并结束正则表达式.

但是,这个解决方案有点问题.容忍的HTML解析器不会阻塞attr="val<u>e".我们会.此外,第一个</form>是匹配的,这在我们有嵌套表单的情况下是不希望的.虽然在这个用例中没有问题,但是当匹配<div>s等时,这个正则表达式是完全无用的.

Regexp Grammars

Perl正则表达式非常强大,允许您声明递归语法.内置语法有点笨拙,但我建议Regexp::Grammars模块轻松完成.更好的是,只需使用已经存在的完全成熟的HTML Parser.

获得比赛

不鼓励使用$&(和$`$'),因为它使perl非常低效.这不会在一个小脚本中表现出来,但无论如何它的风格都不好.而是将整个正则表达式用parens包围以捕获匹配

m{ ( <form [^>]* > .*? </form> ) }
Run Code Online (Sandbox Code Playgroud)

然后使用$1.

perlretut教程可能是理解Perl正则表达式的一个很好的介绍.