【问题标题】:Regular Expressions: about Greediness, Laziness and Substrings正则表达式:关于贪婪、懒惰和子字符串
【发布时间】:2011-03-02 05:56:25
【问题描述】:

我有以下字符串:

123322

理论上,regex 1.*2 应该匹配以下内容:

  • 12(因为*可以是零个字符)
  • 12332
  • 123322

如果我使用正则表达式 1.*2 它匹配 123322
使用1.*?2,它将匹配12

有没有办法匹配12332

完美的做法是在字符串中获取所有可能的匹配项(无论一个匹配项是否是另一个匹配项的子字符串

【问题讨论】:

  • 您确定 1.*?2 与 123322 匹配吗?我原以为它只匹配 12。
  • @Duniyadnd 你是对的。我修好了。
  • 如果字符串中有多个1 怎么办?如果你想要一个简单的1.*2 模式,你可以简单地找到1 的所有位置和2 的所有位置,然后选择一个大于另一个的所有位置——在这种情况下没有理由使用正则表达式。我的回答对这个问题有更一般的方法,但我可能把它复杂化了。 :)

标签: regex


【解决方案1】:

不,除非在正则表达式中添加了其他内容来阐明它应该做什么,否则它将是贪婪的或非贪婪的。中间没有;)

【讨论】:

    【解决方案2】:
    1(.*?2)*$
    

    您将有多个捕获,您可以将它们连接起来以形成所有可能的匹配

    请看这里:regex tester

    点击“表格”并展开捕获树

    【讨论】:

    • 你是对的。不知道为什么我没想到。我将拥有与第 1 组中的捕获一样的匹配项;)
    【解决方案3】:

    您需要为每种情况使用一个单独的表达式,具体取决于您要匹配的二的数量:

    1(.*?2){1}   #same as 1.*?2
    1(.*?2){2}
    1(.*?2){3}
    ...
    

    【讨论】:

    • 好主意。一个简单的循环就足够了,从 1 个 util 开始,它没有找到匹配项;)
    • 现在我看到了@Kobi 的答案,这只有在每个匹配项都是独立的情况下才有效(例如:123312321(.*?2){2} 应该匹配1233121232,但是有一个公共字符串其中:12)。当然,这与一般的正则表达式有关,与您的答案无关;)
    【解决方案4】:

    一般来说,这是不可能的。正则表达式匹配引擎并非真正设计用于查找重叠匹配。一个快速的解决方案是手动检查所有子字符串的模式:

    string text = "1123322";
    for (int start = 0; start < text.Length - 1; start++)
    {
        for (int length = 0; length <= text.Length - start; length++)
        {
            string subString = text.Substring(start, length);
            if (Regex.IsMatch(subString, "^1.*2$"))
                Console.WriteLine("{0}-{1}: {2}", start, start + length, subString);
        }
    }
    

    工作示例:http://ideone.com/aNKnJ

    现在,是否有可能获得完整的正则表达式解决方案?大多数情况下,答案是否定的。然而,.Net 确实有一些技巧可以帮助我们:它允许可变长度的后视,并允许每个捕获组记住所有捕获(大多数引擎只返回每个组的最后一个匹配项)。滥用这些,我们可以在正则表达式引擎中模拟相同的for 循环:

    string text = "1123322!";
    string allMatchesPattern = @"
    (?<=^       # Starting at the local end position, look all the way to the back
    (
      (?=(?<Here>1.*2\G))?  # on each position from the start until here (\G),
      .                     # *try* to match our pattern and capture it,
    )*                      # but advance even if you fail to match it.
    )
    ";
    
    MatchCollection matches = Regex.Matches(text, allMatchesPattern,
                RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace);
    foreach (Match endPosition in matches)
    {
        foreach (Capture startPosition in endPosition.Groups["Here"].Captures)
        {
            Console.WriteLine("{0}-{1}: {2}", startPosition.Index,
                              endPosition.Index - 1, startPosition.Value);
        }
    }
    

    请注意,目前存在一个小错误 - 引擎不会尝试匹配最后一个结束位置 ($),因此您会丢失一些匹配项。目前,在字符串末尾添加 ! 即可解决该问题。

    工作示例:http://ideone.com/eB8Hb

    【讨论】:

    • 这也是有效的。事实上,这与我在应用程序的另一部分所做的非常相似,但这里的问题是它是O(n^2*m)mRegex.IsMatch 的成本)。 m 应该是 O(n),因为它是自动化的。 1123322 没有问题,但是从文件中提取的文本可能有点大,而且效率不高,但是如果客户提出要求,我还是需要这样做:/
    • @Oscar - 正如您所说,假设正则表达式比您描述的更复杂,我看不出如何避免高度复杂性。您可能会针对您的正则表达式做一些特定的事情来优化它,但在更糟糕的情况下,您会创建 all possible substrings,因此您不妨迭代它们:如果在您的代码或正则表达式引擎。 (另外,我把你的问题误认为是一个理论问题,而不是一个实际问题,我不认为我会给我的客户那个正则表达式:)
    猜你喜欢
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-05
    相关资源
    最近更新 更多