为什么这个正则表达式会变慢?

时间:2016-06-08 05:08:37

标签: regex perl backtracking

use WWW::Mechanize; my $mech = new WWW::Mechanize; $mech->get("http://www.elaws.gov.bw/desplaysubsidiary.php?m=SUBSIDIARY&v=I&vp=&id=904"); $page = $mech->content(); $page =~ m/.+?(\d{4})<\/i>\)/s;

当我在一些正常大小的HTML页面上运行时,上面的正则表达式很慢?为什么?我不会认为它应该运行缓慢。

编辑:这是一个演示问题的代码示例:

.+?

正则表达式线需要永远。如果我删除{{1}},则没有延迟。

2 个答案:

答案 0 :(得分:5)

似乎对此有一些误解

假设我们有一个字符串

my $s = 'xxxxxxxxxx9999</i>)';

然后像这样的模式匹配

$s =~ m<.*?(\d{4})</i>\)>

首先假设.*?在字符串的开头处占用无字符。然后它将检查(\d{4})</i>\)是否与该点的字符串匹配

它失败,因此正则表达式引擎向x提供单个字符.*?并再次尝试。这也失败了,因此.*?消耗的字符串部分逐字符扩展,直到匹配十个字符xxxxxxxxxx。此时,模式的其余部分成功匹配,并且正则表达式测试被声明为成功

如果我们有非懒惰模式

$s =~ m<.*(\d{4})</i>\)>

首先假设.*占用所有字符串

此时模式的其余部分不匹配,因此再次开始回溯,只给出.*字符串中的一个字符并再次尝试

这和以前一样重复,但缩短匹配逐个字符,直到找到匹配字符串9999</i>)的后九个字符并且{ {1}}现在与之前的.*匹配

当发现匹配失败时,

回溯 返回转换为先前匹配的模式元素,从而更改该元素的匹配方式再试一次。它没有向后通过对象字符串寻找

这里的问题是由于xxxxxxxxxx必须在模式中考虑。如果我们只有.*?,那么根本就没有回溯。正则表达式引擎只搜索m<(\d{4})</i>\)>并找到它或它不是

只要它是您想要的模式的第一个出现,这样就可以正常工作。不幸的是,找到子串的 last 出现的唯一方法是在它之前加上\d{4}</i>\),这将启动回溯并使进程必然更慢

  

当我在一些正常大小的HTML页面上运行时,上面的正则表达式很慢?

即便如此,根据您对“正常大小的HTML页面”的看法,我看不到这花费的时间超过几毫秒。正则表达式引擎用C编码,写得非常快。我想你必须在它上面运行一个计时器才能注意到任何延迟?

答案 1 :(得分:2)

这个正则表达式很慢,因为你将result = from tr in dbContext.Journals select tr; if (!String.IsNullOrEmpty(SearchParam1)) { result = result.Where(tr => tr.Field1.StartsWith(SearchParam1)); } if (!String.IsNullOrEmpty(SearchParam2)) { result = result.Where(tr => tr.Field2.StartsWith(SearchParam2)); } .一起引入了懒惰。对于一个20行长的html hello world,它从60(贪婪时)到2000(懒惰时)的步骤增加。

想象一下它将如何处理&#34; 一些正常大小的HTML页面&#34;。您可以测试here(在 regex调试器下)。

另请查看why you shouldn't use regex to parse html