【问题标题】:Regular Expression nongreedy is greedy正则表达式 nongreedy 是贪婪的
【发布时间】:2010-09-19 18:16:31
【问题描述】:

我有以下文字

tooooooooooooon

根据我正在阅读的这本书,当? 跟在任何量词之后时,它变得不贪心。

我的正则表达式 to*?n 仍在返回 tooooooooooooon

它应该返回ton 不是吗?

知道为什么吗?

【问题讨论】:

    标签: regex regex-greedy non-greedy


    【解决方案1】:

    Regex 总是尽力匹配。在这种情况下,您唯一要做的就是通过让解析器回溯到 /o*?/ 节点来减慢解析器的速度。 "tooooon" 中的每个 'o' 一次。而在正常匹配的情况下,第一次需要尽可能多的'o's。由于下一个要匹配的元素是'n',它不会被'o' 匹配,因此尝试使用最小匹配没有什么意义。实际上,当正常匹配失败时,它需要相当长的时间才能失败。它必须通过每个'o' 回溯,直到没有人可以回溯。在这种情况下,我实际上会使用最大匹配/to*+n/'o' 会尽其所能,永远不会退还任何东西。这将使它在失败时迅速失败。

    最小 RE 成功:

    'toooooon' ~~ /to*?n/ t o o o o o o o o o o o o {t} 匹配 [t] [t] 匹配 [o] 0 次 [t] 匹配失败 [n] -> 重试 [o] [t]{o} 匹配 [o] 1 次 [t][o] 匹配失败 [n] -> 重试 [o] [t][o]{o} 匹配 [o] 2 次 [t][o][o] 匹配失败 [n] -> 重试 [o] . . . . [t][o][o][o][o]{o} 匹配 [o] 5 次 [t][o][o][o][o][o] 匹配失败 [n] -> 重试 [o] [t][o][o][o][o][o]{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o]{n} 匹配 [n]

    正常 RE 成功:

    (注意:Maximal RE 类似)

    '太棒了' ~~ /to*n/ t o o o o o o 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]

    最小 RE 失败:

    'toooooo' ~~ /to*?n/ t o o o o o . . . . . . . . [t][o][o][o][o]{o} 匹配 [o] 5 次 [t][o][o][o][o][o] 匹配失败 [n] -> 重试 [o] [t][o][o][o][o][o]{o} 匹配 [o] 6 次 [t][o][o][o][o][o][o] 匹配失败 [n] -> 重试 [o] [t][o][o][o][o][o][o] 匹配失败 [o] 7 次 -> 匹配失败

    正常 RE 失败:

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

    最大 RE 失败:

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

    【讨论】:

      【解决方案2】:

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

      你的表情是这样说的:

      一个“t”,后跟*尽可能少的*“o”,然后是一个“n”。

      这意味着任何必要的 o 都将被匹配,因为末尾有一个 'n',表达式渴望达到。匹配所有的o是成功的唯一可能。

      【讨论】:

        【解决方案3】:

        正则表达式尝试匹配其中的所有内容。因为要匹配的 'o' 不比匹配 n 的每个 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
        

        【讨论】:

        • 我会使用相同的字符串进行测试。更清楚您要强调的内容。
        【解决方案4】:

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

        因为子字符串 'ton' 不存在于字符串中的任何位置,它不可能是匹配的结果。匹配只会返回原始字符串的子字符串

        编辑:为了清楚起见,如果您使用下面的字符串,则带有一个额外的 'n'

        toooooooonoooooon
        

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

        t.*n
        

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

        toooooooonoooooon
        

        但是正则表达式

        t.*?n
        

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

        toooooooon
        

        【讨论】:

        • 您也可以使用正则表达式来替换,但它只会替换匹配项。
        • 解释得很清楚!我认为您的最后一个示例应该从 "t.*?n" 更改为 "to*?n" - 因为它是 "t.*?n" 匹配整个字符串。详情见我的回答。
        • Adam:不,用你最喜欢的语言自己尝试一下——我用的是rubular.com/regexes/4760——你的应该是一样的。全部 *?要做的是找到最短的匹配而不是最长的匹配。
        【解决方案5】:

        您正在搜索的字符串(实际上是大海捞针)不包含子字符串“ton”。

        但它确实包含子字符串“tooooooooooooon”。

        【讨论】:

        • 除此之外,您的正则表达式只能返回它可以找到的内容。它无法在您的字符串中找到“ton”,因为它不存在,但它可以找到“tooooooooooooon”。
        • 很好的评论,为了清楚起见,我会添加。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多