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

时间:2012-08-19 21:54:52

标签: regex perl match lookahead

根据下面的代码,我想匹配第一个form次出现。我发现负面预测?!可能会用来实现这一点,但它不起作用。我的正则表达式出了什么问题?

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

1 个答案:

答案 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等时,这个正则表达式完全没用。

Regexp Grammars

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

获取比赛

不鼓励使用$`(以及$'m{ ( <form [^>]* > .*? </form> ) } ),因为它会使perl极其低效。这不会在一个小脚本中表现出来,但无论如何它的坏样式。而是将整个Regexp与parens一起包含在捕获匹配

$1

然后使用{{1}}。

perlretut Tutorial可能是理解Perl正则表达式的一个很好的介绍。