我有以下脚本,它抓取一个网页,然后做一个正则表达式来查找我正在寻找的项目:
use warnings;
use strict;
use LWP::Simple;
my $content=get('http://mytempscripts.com/2011/09/temporary-post.html') or die $!;
$content=~s/\n//g;
$content=~s/ / /g;
$content=~/<b>this is a temp post<\/b><br \/><br \/>(.*?)<div style='clear: both;'><\/div>/;
my $temp=$1;
while($temp=~/((.*?)([0-9]{1,})(.*?)\s+(.*?)([0-9]{1,})(.*?)\s+(.*?)([0-9] {1,})(.*?)\s+)/g){
print "found a match\n";
}
这样可行,但需要很长时间。当我将正则表达式缩短到以下时,我得到的结果不到一秒钟。为什么我的原始正则表达式需要这么长时间?我该如何纠正?
while($temp=~/((.*?)([0-9]{1,})(.*?)\s+(.*?)([0-9]{1,})(.*?)\s+(.*?)([0-9] {1,})(.*?)\s+)/g){
print "found a match\n";
}
答案 0 :(得分:1)
正则表达式类似于Perl中的sort
函数。你认为这很简单,因为它只是一个命令,但最后,它使用了大量的处理能力来完成这项工作。
您可以采取一些措施来帮助解决问题:
.*
)。 这个可怜的事实是,在Perl写了几十年后,我从来没有掌握正则表达式解析的深层秘密。我已多次尝试去理解它,但这通常意味着在网上进行研究,而且......好吧......我被网络上的所有其他东西分散了注意力。
并且,并不是那么困难,任何半智商的智商为240,并且对虐待狂的偏爱应该很容易就能找到它。
@David W。:我想我对回溯感到困惑。我不得不多次阅读你的链接,但仍然不太了解如何在我的情况下实现它(或者,不实现它)。 - user522962
我们举一个简单的例子:
my $string = 'foobarfubar';
$string =~ /foo.*bar.*(.+)/;
my $result = $1;
$result
会是什么?它将是r
。你看这是怎么回事?让我们看看会发生什么。
最初,正则表达式被分解为标记,并使用第一个标记foo.*
。这实际上匹配整个字符串:
"foobarfubar" =~ /foo.*/
但是,如果第一个正则表达式标记捕获整个字符串,则正则表达式的其余部分将失败。因此,正则表达式匹配算法必须回溯:
"foobarfubar" =~ /foo.*/ #/bar.*/ doesn't match
"foobarfuba" =~ /foo.*/ #/bar.*/ doesn't match.
"foobarfub" =~ /foo.*/ #/bar.*/ doesn't match.
"foobarfu" =~ /foo.*/ #/bar.*/ doesn't match.
"foobarf" =~ /foo.*/ #/bar.*/ doesn't match.
"foobar" =~ /foo.*/ #/bar.*/ doesn't match.
...
"foo" =~ /foo.*/ #Now /bar.*/ can match!
现在,字符串的其余部分也是如此:
"foobarfubar" =~ /foo.*bar.*/ #But the final /.+/ doesn't match
"foobarfuba" =~ /foo.*bar.*/ #And the final /.+/ can match the "r"!
.*
和.+
表达式往往会发生回溯,因为它们太松散了。我看到你正在使用非贪婪的比赛,这可能有所帮助,但如果你不小心,它仍然是一个问题 - 特别是如果你有非常长而复杂的正则表达式。
我希望这有助于解释回溯。
您遇到的问题不是您的程序不起作用,而是需要很长很长时间。
我希望我的答案的一般要点是正则表达式解析不像Perl那样简单。我可以在程序中看到命令sort @foo;
,但忘记如果@foo
包含大约一百万个条目,则可能需要一段时间。理论上,Perl可以使用冒泡排序,因此算法是O 2 。我希望Perl实际上使用更有效的算法,我的实际时间将更接近O * log(O)。但是,所有这一切都被我简单的一行声明所隐藏。
我不知道回溯是否是您的问题,但是您将整个网页输出视为单个字符串以匹配正则表达式,这可能导致非常长的字符串。您尝试将其与您反复执行的另一个正则表达式进行匹配。显然,这是一个过程密集的步骤,它被一个Perl语句隐藏(很像sort @foo
隐藏其复杂性)。
在周末开始思考这个问题,你真的不应该尝试用正则表达式解析HTML或XML,因为它太邋。了。你最终会得到一些相当低效和脆弱的东西。
在这样的情况下,最好使用我更熟悉的HTML::Parser或XML::Simple之类的内容,但不一定适用于格式不正确的HTML。
Perl正则表达式很好,但它们很容易摆脱我们的控制。
答案 1 :(得分:0)
您可能尝试的一件事是将所有捕获组(...)更改为非捕获组(?:...)
如果您需要打印出“找到匹配”,这将为匹配器节省一些精力,但如果您的真实代码更多,我不确定您是否可以实现这一点。
另外,一般来说,有很多通配符如(。*?)只会增加我认为的重量,所以也许知道你想要匹配的东西你能消除其中的一些吗?我不能肯定地说;在这里看不到任何纯正式的优化。