4

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

<!-- 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 -->

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

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

它失败了,因为正则表达式匹配 first<!--和 last之前的所有内容-->。然后我尝试使用另一种带有前瞻断言的模式:

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

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

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

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

更新:

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

更新:

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

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

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

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

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

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

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

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

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

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

4

2 回答 2

2

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

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

这个执行以下操作:

  • 捕获<!--
  • 向前看,捕获字符,包括stylesheet. 找不到就失败。
  • 捕获字符,包括-->.
<!--(?=.*stylesheet).*?-->/s

这个执行以下操作:

  • 捕获<!--
  • 向前看,捕捉任何角色,直到不再可能。回溯,不断尝试匹配stylesheet。找不到就失败。
  • 捕获字符,包括-->.

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

如果您的主题是...

<!-- 敏捷的棕狐
              跳过懒狗 -->

<!--[如果 IE 7]>
    <link rel=" stylesheet " type="text/css" href="/supersheet.css" /> <![endif]-->
<!-- 用五打样式表
打包我的盒子-->

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

于 2015-08-16T01:53:57.697 回答
2

只匹配部分<!--……有很多方法stylesheet-->

1.) 使用否定的连字符[^-] 来限制匹配并保持在<!--和之间stylesheet

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

[^-]只允许字符,不是连字符。请参阅regex101 处的测试


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

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

请参阅regex101 处的测试。也可以使用捕获组并获取 $1: (?s)^.*(<!--.+?stylesheet.+?-->)


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

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

请参阅regex101 处的测试(?!<!).向前看中间的每个字符,<!--如果stylesheet没有开始另一个<!......留在一个元素内。类似于否定的连字符解决方案。


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

于 2015-08-16T08:01:01.840 回答