4

我正在看一个关于正则表达式的教程。

这是关于如何从这段 html 中获取 class 属性

<pre class="ruby" name="code">

并且使用的正则表达式是

<pre class="([^"]+)" name="code">

他们建议使用上述的而不是

<pre class="(.+)" name="code">

“因为它超出了报价范围。”

我不明白他们的意思。无论如何它都会起作用,但是为什么推荐第一个正则表达式。我错过了什么吗?请赐教。

提前致谢。

4

4 回答 4

4

.+贪婪地匹配。例如,在

<pre class="ruby" size="medium" name="code"> 

它会匹配ruby" size="medium。更糟糕的是,如果你在同一行上有两个标签,它会在标签边界上匹配:

<pre class="ruby" name="code">foo</pre> <pre class="python" name="code">bar</pre>

会导致ruby" name="code">foo</pre> <pre class="python

因此,只要您确切地知道您的 HTML 会是什么样子,.+就可以工作,但是一旦它意外更改(因为 HTML 不会这样做),您的正则表达式不会简单地失败(就像第二个那样),但它会匹配错误的东西。

因此,第二个正则表达式更安全(因为它更明确地说明了允许匹配的内容)。您通常应该尽量避免简单.+.*“匹配任何东西”,而是考虑您想要匹配的内容。

也就是说,出于完全相同的原因,您不应该尝试将 HTML 和其他标记语言与正则表达式进行匹配,因为有更好的工具可以做到这一点。

于 2012-04-08T07:54:42.480 回答
2

正则表达式匹配通常会尝试匹配最长的正则表达式。因此 "([^"]+)" 只匹配它遇到的第一个引号。另一方面,"(.+)" 将从第一个引号匹配到字符串中的最后一个引号。

例如,如果我们将它们应用于您的问题,第一个将 match "ruby",因为这是您问题中第一个引用的字符串。第二个将从"rubyon 一直匹配到beyond the quote",因为这是问题中的最后一个引号(并且会在其间包含其他几个带引号的字符串。

于 2012-04-08T07:54:44.350 回答
2

考虑这个例子:

<pre class="scooby" name="not-code">
  content
</pre>

...other HTML...

<pre class="ruby" name="code">
  content
</pre>

使用此正则表达式 [*]:

<pre class="(.+)" name="code">

...第一部分 - <pre class="- 开始匹配第一个标签,然后(.+)消耗整个文档的其余部分。但是正则表达式的其余部分 - " name="code">- 不能在那里匹配,所以它会后退,直到找到它可以的位置 - 在第二个标签中。结果:该组最终捕获了从scooby到的所有内容ruby

即使您使用 non-greedy(.+?)而不是 greedy 也是如此(.+)。人们经常说非贪婪量词导致正则表达式返回最短的匹配,但事实并非如此。就像一个贪婪的正则表达式,它一有机会就开始匹配;它也会尽快停止匹配。像这种非贪婪量词没有用的情况并不少见。

另一件要考虑的事情是当没有可能的匹配时——例如,如果有<pre>第一个属性的标签class="~whatever~",但没有一个name="code"属性的标签。每一次,贪婪(.+)者都会吞噬整个文件,然后退缩,直到到达起点,然后放弃。非贪婪(.+?)不会回溯,但它会扫描整个页面,并且会慢得多(它有效地对" name="code">每个位置进行前瞻)。

使用这个正则表达式:

<pre class="([^"]+)" name="code">

...它永远不必扫描超出标签的末尾来确定它是否匹配。

始终考虑如果无法匹配会发生什么。这可能是正则表达式作者最常见的监督,也是导致性能问题最多的一种。

[*] 出于说明目的,我假设比赛是在 DOTALL 模式(又名单行模式)下进行的。

于 2012-04-08T11:05:50.323 回答
1

否定一个类通常更具体地说明你想要匹配的内容,并且可以帮助防止像Catastrophic Backtracking这样的情况。

Jeff Atwood 不久前写了一篇关于它的有趣博客文章,其中他举了一个看似无辜的正则表达式的例子:(x+x+)+y这可能需要(几乎)永远完成处理。即使主题很小,像这样:xxxxxxxxxxxxxxxxxxxx.

给它读一读,它真的很有趣。

于 2012-04-08T10:52:02.257 回答