已经回答了:
这个正则表达式有效:
<item>(?:(?!</item>).|\n)*?(?:(?=201[0-3]</pubDate>))(?:(?!</item>).|\n)*?</item>
虽然这个崩溃了堆栈:
<item>(?:(?!</item>).|\n)*(?:(?=201[0-3]</pubDate>))(?:(?!</item>).|\n)*</item>
这也有效,没有前瞻:
(?s)<item>.*?201[0-3]</pubDate>.*?</item>
原始问题:
我在Sublime Text 2中有一个XML文件(下面的例子)。我想找到所有<item
&gt;包含<pubDate
&gt;的元素2010年至2013年的元素。
上面的正则表达式工作正常,但是当我找到所有(文件大约1MB,大约120个匹配)时,ST2用完了堆栈空间。
潜伏在哪些可怕的低效率?
示例XML:
<?xml version="1.0" encoding="utf-8"?>
<channel>
<item>
<title>This will match</title>
<link>http://gcanyon.posterous.com/</link>
<pubDate>Sat Mar 10 10:22:00 -0800 2012</pubDate>
<dc:creator><![CDATA[Geoff Canyon]]></dc:creator>
</item>
<item>
<title>This won't</title>
<link>http://gcanyon.posterous.com/</link>
<pubDate>Tue Jun 30 05:01:32 -0700 2009</pubDate>
<dc:creator><![CDATA[Geoff Canyon]]></dc:creator>
</item>
</channel>
</rss>
答案 0 :(得分:2)
贪婪的贪婪正则表达式。例如:
(?:(?!</item>).|\n)*
会一直到下一个</item>
,而它不是你想要的,你只是不希望它走得更远,我会假设。
你应该在lazy operators找到快乐。
PS:抱歉,我没有足够的时间深入了解你的正则表达式。希望它能解决你的问题。
答案 1 :(得分:2)
我认为你有两个问题。一个是你的整个方法(如果你只是想要我的真实建议,跳到底部),但看起来另一个是catastrophic backtracking。
如果我们稍微简化你的模式,可以归结为:
{a}{x*}{x*}{b}
注意两个x*
彼此相邻?是的,它们之间有一个(?=y)
,但让我们忽略它一分钟,因为我不认为引擎正在有效地使用它来限制它正在做的工作量。假设您有一个类似axxxxxxxb
的字符串,并且您希望将其与模式匹配。由于有两个x*
令牌彼此相邻,因此引擎无法轻易判断一个组的结束位置和另一个组的开始位置。所以它试图将它们全部放在第一个{x*}
桶中,因为*
是贪婪的:
{a}{xxxxxxx}{}{b}
很好,对吗?它匹配,所以我们可以继续前进。但请考虑axxxxxxQxb
之类的内容。这在第一遍时不匹配,因此引擎必须不断尝试排列:
{a}{xxxxxxx}{}{Q} #nope
{a}{xxxxxx}{x}{Q} #nope
{a}{xxxxx}{xx}{Q} #nope
...
最终,这需要很长时间才会炸毁你的筹码。
那么如何解决呢?嗯,就是这样:
(?:(?=201[0-3]</pubDate>))
我认为如果引擎是肯定的代币,而不是前瞻,引擎会做得更好。无论如何,它不需要是一个先行;你可以使用它(有或没有\s*
):
201[0-3]\s*</pubDate>
之后的(?:(?!</item>).)*
是多余的;你应该只需要一个懒惰的.*?
。
此外,您可以使用多行选项使.
也匹配换行符,但我不确定这是否会在速度/执行方面产生任何影响。但是,写作会更短。
整个事情看起来像是:
<item>(?:(?!</item>).)*?201[0-3]</pubDate>.*?</item> #plus the /m flag
但我认为真正的问题是你using regex at all。这看起来像XML。你为什么不使用XML解析器?如果您使用的是.NET,LINQ to XML非常适合您所描述的确切工作,包括有关嵌套pubdate
中特定值的部分。应该比正则表达式更简单,更有效。