正则表达式的非贪婪模式是贪婪的。

7

我有以下文本

tooooooooooooon

根据我正在阅读的书籍,当“?”跟随在任何量词后面时,它会变成非贪婪模式。
我的正则表达式“to*?n”仍然返回“tooooooooooooon”。
它应该返回“ton”,不是吗?
你知道为什么吗?
5个回答

46

正则表达式只能匹配实际存在的文本片段。

由于子字符串“ton”在您的字符串中不存在,因此不能作为匹配结果。匹配只会返回原始字符串的子串

编辑:明确一下,如果您使用以下带有额外“n”的字符串:

toooooooonoooooon

这个正则表达式(不指定'o')

t.*n

会匹配以下内容(在“n”之前尽可能多的字符)

toooooooonoooooon

但是正则表达式

t.*?n

只会匹配以下内容(在“n”之前尽可能少的字符)

toooooooon

你也可以使用正则表达式进行替换,但它只会替换匹配的内容。 - Ady
非常清晰的解释!我认为你最后一个例子应该从“t.?n”改为“to?n”——因为它匹配整个字符串。请参阅我的答案以获取详细信息。 - Adam Liss
1
Adam:不,用你最喜欢的语言自己试试吧——我使用了http://www.rubular.com/regexes/4760——你的应该也一样。*?只是查找最短的匹配项,而不是最长的匹配项。 - Gareth

5

正则表达式总是渴望匹配的。

你的表达式是这样的:

一个 't',后跟 *尽可能少的* 'o',再后面是一个 'n'。

这意味着任何必要的 o 都将被匹配,因为在结尾处有一个 'n',而表达式渴望到达该位置。匹配所有的 o 是它唯一成功的可能性。


4
正则表达式试图匹配其中的所有内容。因为要匹配的“n”旁边有不少于toooon中每个“o”的数量的'o',所以会匹配所有内容。此外,由于您使用的是o*?而不是o+?,因此不需要存在'o'。
例如,在Perl中:
$a = "toooooo";
$b = "toooooon";

if ($a =~ m/(to*?)/) {
        print $1,"\n";
}
if ($b =~ m/(to*?n)/) {
        print $1,"\n";
}

~>perl ex.pl
t
toooooon

我会使用相同的字符串进行测试。这样可以更清楚地突出你想要强调的内容。 - Brad Gilbert

4
正则表达式总是尽力匹配。在这种情况下,您所做的唯一事情就是通过使解析器回溯到/o*?/节点中来减慢其速度。对于“tooooon”中的每个'o',都要回溯一次。而使用普通匹配,它将尽可能多地获取'o',第一次通过时需要尽可能多的'o'。由于下一个要匹配的元素是'n',无法与'o'匹配,因此尝试使用最小匹配没有什么意义。实际上,当正常匹配失败时,它会花费很长时间才能失败。它必须回溯到每个'o',直到没有任何东西可以回溯。在这种情况下,我实际上会使用最大匹配/to*+n/。'o'将尽可能多地获取,并且永远不会放回。这将使它在失败时快速失败。
最小RE成功:
'toooooon' ~~ /to*?n/
t o o o o o o n {t} 匹配 [t] [t] 匹配 [o] 0 次 [t]<n> 无法匹配 [n] -> 重试 [o] [t]{o} 匹配 [o] 1 次 [t][o]<n> 无法匹配 [n] -> 重试 [o] [t][o]{o} 匹配 [o] 2 次 [t][o][o]<n> 无法匹配 [n] -> 重试 [o]
. . . .
[t][o][o][o][o]{o} 匹配 [o] 5 次 [t][o][o][o][o][o]<n> 无法匹配 [n] -> 重试 [o] [t][o][o][o][o][o]{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]{n} 匹配 [n]
普通RE成功:
(注意:最大RE类似)
'toooooon' ~~ /to*n/
t o o o o o o n {t} 匹配 [t] [t]{o}{o}{o}{o}{o}{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]{n} 匹配 [n]
最小RE失败:
'toooooo' ~~ /to*?n/
t o o o o o o
. . . .
. . . .
[t][o][o][o][o]{o} 匹配 [o] 5 次 [t][o][o][o][o][o]<n> 无法匹配 [n] -> 尝试匹配 [o] [t][o][o][o][o][o]{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]<n> 无法匹配 [n] -> 尝试匹配 [o] [t][o][o][o][o][o][o]<o> 无法匹配 [o] 7 次 -> 匹配失败

普通 RE 的失败:

'toooooo' ~~ /to*n/
t o o o o o o {t} 匹配 [t] [t]{o}{o}{o}{o}{o}{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]<n> 无法匹配 [n] -> 尝试匹配 [o] [t][o][o][o][o][o] 匹配 [o] 5 次 [t][o][o][o][o][o]<n> 无法匹配 [n] -> 尝试匹配 [o]
. . . .
[t][o] 匹配 [o] 1 次 [t][o]<o> 无法匹配 [n] -> 尝试匹配 [o] [t] 匹配 [o] 0 次 [t]<n> 无法匹配 [n] -> 匹配失败

最大化 RE 的失败:

'toooooo' ~~ /to*+n/
t o o o o o o {t} 匹配 [t] [t]{o}{o}{o}{o}{o}{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]<n> 无法匹配 [n] -> 匹配失败

2
您要搜索的字符串(也就是干草垛)不包含子字符串“ton”。但它确实包含子字符串“tooooooooooooon”。

此外,您的正则表达式只能返回它能找到的内容。它无法在您的字符串中找到“ton”,因为它不存在,但它可以找到“tooooooooooooon”。 - Matthew Scharley
好的评论,我会加上以增加清晰度。 - Hank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接