preg_match_all - 正则表达式的贪婪部分,但最大化匹配数

时间:2011-03-02 21:46:54

标签: php regex preg-match-all regex-greedy

我有以下要解析的html:

<h1 class="x">test</h1>
<p>some text <img src="x" /></p>

<h1 class="x1">test2</h1>
<p>some text </p>

<h1 class="2">test3</h1>
<p>some text <img src="x" /></p>

我可以将它解析为带有单个正则表达式的数组吗?

我试过

preg_match_all('#(<h1[^>]*?>)(.*?)(</h1>)(.*)#ism',$html,$arr);

只给我一个条目,因为正则表达式的最后一部分是贪婪的,而

preg_match_all('#(<h1[^>]*?>)(.*?)(</h1>)(.*?)#ism',$html,$arr);

它没有给我<h1>之间的HTML,因为表达式并不贪婪。

如何在匹配贪婪之后制作零件,同时尽可能多地匹配?

补充意见:

  • 这个问题相当具有学术性,我已经使用pre_split解决了这个问题,并且各种其他方法都可以使用,但也可能有缺点(例如DOM可能无效,我无法控制无效的HTML)。然而,这是一个反复出现的问题,我有兴趣了解更多。

2 个答案:

答案 0 :(得分:4)

你需要某种形式的终结者。正则表达式无法猜测,直到您要匹配哪个部分。

在这种情况下可能是最后(.*?)之后的先行断言:

(?=<h1|</body>|\z)#ims

答案 1 :(得分:1)

忽略有关正则表达式不合适的评论,因为它仍然是一个有趣的问题,有两种方法可以解决这个问题:贪婪和懒惰。

模式的各个部分是:

  • 懒惰.*?(?=<h1|\z)
  • 贪心(?:[^<]+|<(?!h1))*

您可能熟悉the performance of greedy vs. lazy qualifiers in general,但这里的关键要简单得多。

如果你想要匹配的字符串完全由字符<组成,那么懒惰和贪婪的模式将执行大致相同的操作,因为它们都必须检查每个匹配字符的断言。

然而,在HTML中你有比<个字符更多的其他字符,所以贪婪的模式,不必检查其他字符,可以快几个数量级。

我承认懒惰模式更容易阅读,但我认为更好的性能是值得的,并且强烈建议您使用x修饰符评论您的模式。