【问题标题】:RegExp exercise: reluctant quantifier with a lookahead assertion正则表达式练习:带有前瞻断言的不情愿量词
【发布时间】:2015-08-16 01:34:01
【问题描述】:

你能解释一下这是如何工作的吗?这是一个例子:

<!-- The quick brown fox 
              jumps over the lazy dog -->

<!--[if IE 7]>
    <link rel="stylesheet" type="text/css" href="/supersheet.css" />
<![endif]-->

<!-- Pack my box with five dozen liquor jugs -->

首先,我尝试使用以下正则表达式来匹配条件 cmets 内的内容:

/<!--.*?stylesheet.*?-->/s

它失败了,因为正则表达式匹配第一个 &lt;!-- 和最后一个 --&gt; 之前的所有内容。然后我尝试使用另一种带有前瞻断言的模式:

/<!--(?=.*?stylesheet).*?-->/s

它可以工作并且完全符合我的需要。但是,以下正则表达式也可以:

/<!--(?=.*stylesheet).*?-->/s

最后一个正则表达式在前瞻断言中没有不情愿的量词。现在我很困惑。谁能解释一下它是如何工作的?也许这个例子有更好的解决方案?

更新:

我尝试在另一个文档中使用带有前瞻断言的正则表达式,但它无法匹配 cmets 之间的内容。所以,这个/&lt;!--(?=.*?stylesheet).*?--&gt;/s(以及这个/&lt;!--(?=.*stylesheet).*?--&gt;/s)是不正确的。不要使用它并尝试其他建议。

更新:

Jonny 5 找到了解决方案(请参阅答案)。他提出了三个选择:

  1. 使用否定的连字符来限制匹配。仅当标签之间没有连字符时,此选项才有效。如果样式表的 URL 为 /style-sheet.css,它将不起作用。
  2. 使用转义序列:\K。它就像一个魅力。缺点如下:
    • 非常慢(在我的情况下,它比其他解决方案慢 8-10 倍)
    • 仅自 PHP 5.2.4 起可用
  3. 使用前瞻来缩小匹配范围。这是我试图实现的目标,但我使用环视断言的经验不足以执行任务。

我认为以下是我的示例的一个很好的解决方案:

/(?s)<!--(?:(?!<!).)+?stylesheet.+?-->/

相同,但末尾带有 s 修饰符:

/<!--(?:(?!<!).)+?stylesheet.+?-->/s

正如我所说,这是一个很好的解决方案,但我设法改进了这种模式,并找到了另一种在我的情况下工作得更快的方法。

所以,最终的解决方案如下:

/<!--(?:(?!-->).)+?stylesheet.+?-->/s

感谢所有参与者提供有趣的答案。

【问题讨论】:

  • 如果你想在 stylesheet 之前匹配

标签: php regex lookahead reluctant-quantifiers


【解决方案1】:

只匹配&lt;!--...stylesheet...--&gt;的部分有很多方法:

1.) 使用negated 连字符[^-] 来限制匹配并保持在&lt;!--stylesheet 之间

(?s)<!--[^-]+stylesheet.+?-->

[^-] 只允许字符,而不是连字符。见test at regex101


2.) 要获得“最后一个”或最接近的匹配而不需要太多正则表达式,也可以在前面加上 greedy dot 以ᗧ吃掉。如果不匹配全局/仅匹配一项,则有意义。在贪婪之后使用\K to reset

(?s)^.*\K<!--.+?stylesheet.+?-->

test at regex101。也可以使用capture group and grab $1:(?s)^.*(&lt;!--.+?stylesheet.+?--&gt;)


3.) 使用lookahead 缩小范围通常成本更高:

(?s)<!--(?:(?!<!).)+?stylesheet.+?-->

test at regex101(?!&lt;!). 向前看&lt;!--stylesheet 之间的每个字符,如果不开始另一个&lt;!... 留在一个元素内。类似于否定的连字符解决方案。


我用.+ 代替了.* 一个或多个 - 取决于要匹配的内容。这里+ 更适合。
使用什么解决方案取决于确切的要求。对于这种情况,我会使用第一个。

【讨论】:

  • @Mr.twister 欢迎您!我阅读了您的更新-第二种解决方案的输入速度较慢。实际上这应该很快。取决于输入。我进行了编辑并在第二个解决方案中添加了 ^ start anchor 以避免不必要的回溯。对于许多情况,前瞻解决方案会更慢。您对(?!--&gt;). 的修改很好 - 更具体。
【解决方案2】:

字符串stylesheet在您的测试文档中只被提及一次,因此您尝试的两个正则表达式将匹配相同的内容,但方式不同。

<!--(?=.*?stylesheet).*?-->/s

这个有以下作用:

  • 捕获&lt;!--
  • 向前看,捕获直到 stylesheet 并包括在内的字符。未找到则失败。
  • 捕获最多包括--&gt; 在内的字符。
<!--(?=.*stylesheet).*?-->/s

这个有以下作用:

  • 捕获&lt;!--
  • 向前看,捕捉任何角色,直到不再可能。回溯,不断尝试匹配stylesheet。未找到则失败。
  • 捕获的字符不超过--&gt;

基本上,一个人需要大幅回溯,而另一个人不需要。

如果你的主题是……

你会得到两个不同的结果。前者会找到第一个stylesheet,而后者会找到第二个(也是最后一个),因为它从字符串末尾开始搜索。

【讨论】:

  • 非常有趣的答案!谢谢你,@匿名!已找到解决方案。请查看更新后的帖子。
猜你喜欢
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
相关资源
最近更新 更多