【问题标题】:Match at every second occurrence每隔一秒匹配一次
【发布时间】:2009-02-26 08:41:29
【问题描述】:

有没有办法指定一个正则表达式来匹配字符串中模式的每 2 次出现?

例子

  • 针对字符串 abcdabcd 搜索 a 应该会在位置 5 找到一个匹配项
  • 针对字符串 abcdabcd 搜索 ab 应该会在位置 5 找到一个匹配项
  • 针对字符串 abcdabcd 搜索 dab 应该找不到任何匹配项
  • 针对字符串 aaaa 搜索 a 应该会在位置 2 和 4 找到两个匹配项

【问题讨论】:

  • 也许我太挑剔了,但正则表达式不会“找到”任何东西。它只会“匹配”您输入字符串的一部分。它是您的编程语言,它为您提供了将字符串与正则表达式进行匹配并返回有关匹配的各种信息(例如匹配发生的位置)的功能。
  • 你说的对,你太挑剔了;)

标签: regex


【解决方案1】:

使用分组。

foo.*?(foo)

【讨论】:

  • 这似乎对我不起作用。我正在尝试匹配模式的第二次出现,其中第二次匹配的实际内容与第一次不同。我试过|\d{2}/\d{2}/\d{2}.*?(\d{2}/\d{2}/\d{2})|,第一次出现可能是01/01/01,第二次可能是09/03/15,我正在尝试抓住09/03/15
  • @AltimusPrime 正则表达式完美运行。 09/03/15 是匹配的第一个子组。
  • 这确实需要更好的解释
【解决方案2】:

假设你想要的模式是 abc+d。您想匹配该模式在字符串中的第二次出现。

您将构建以下正则表达式:

abc+d.*?(abc+d)

这将匹配以下形式的字符串:<your-pattern>...<your-pattern>。因为我们使用了不情愿的限定符 *?我们很安全,两者之间不会有另一场比赛。使用几乎所有正则表达式实现都提供的匹配器组,然后您将检索括号组中的字符串,这就是您想要的。

【讨论】:

    【解决方案3】:

    如果您使用的是 C#,则可以一次获取所有匹配项(即使用 Regex.Matches(),它返回一个 MatchCollection,并检查项目的索引:index % 2 != 0)。

    如果您想找到替换它的匹配项,请使用Regex.Replace() 的重载之一,该重载使用MatchEvaluator(例如Regex.Replace(String, String, MatchEvaluator)。代码如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                string input = "abcdabcd";
    
                // Replace *second* a with m
    
                string replacedString = Regex.Replace(
                    input,
                    "a",
                    new SecondOccuranceFinder("m").MatchEvaluator);
    
                Console.WriteLine(replacedString);
                Console.Read();
    
            }
    
            class SecondOccuranceFinder
            {
                public SecondOccuranceFinder(string replaceWith)
                {
                    _replaceWith = replaceWith;
                    _matchEvaluator = new MatchEvaluator(IsSecondOccurance);
                }
    
                private string _replaceWith;
    
                private MatchEvaluator _matchEvaluator;
                public MatchEvaluator MatchEvaluator
                {
                    get
                    {
                        return _matchEvaluator;
                    }
                }
    
                private int _matchIndex;
                public string IsSecondOccurance(Match m)
                {
                    _matchIndex++;
                    if (_matchIndex % 2 == 0)
                        return _replaceWith;
                    else
                        return m.Value;
                }
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      会不会像

      (pattern.*?(pattern))*
      

      为你工作?

      编辑:

      问题在于它使用非贪婪运算符*?,这可能需要沿着字符串进行大量回溯,而不是只查看每个字母一次。这对您意味着,对于较大的差距,这可能会很慢。

      【讨论】:

      • 我不确定,Patrick,我会说非贪婪运算符可以使用更少的回溯。当然,这取决于您使用的算法,但是要检查“a.*a”,您必须向上到字符串的末尾并尝试向后匹配,对于“a.*?a”,您可以尝试向前匹配和当你这样做时停下来。
      • Remo.D 是对的:非贪婪量词不会增加回溯,而是消除它。 (它们的效率可能会或可能不会降低,但如果是,也不会是因为回溯。)但在这种情况下,效率无关紧要。正如 annakata 指出的那样,量词必须是非贪婪的,这种方法才能起作用。
      【解决方案5】:

      反向引用可以在这里找到有趣的解决方案。这个正则表达式:

      ([a-z]+).*(\1)
      

      将找到最长的重复序列。

      这会找到一个重复的 3 个字母序列:

      ([a-z]{3}).*(\1)
      

      【讨论】:

      • 这与其他答案略有不同,但您仍然需要使量词非贪婪:/([a-z]+).*?(\1)/跨度>
      【解决方案6】:

      没有这样做的“直接”方式,但您可以指定模式两次,如:a[^a]*a 匹配第二个“a”。

      另一种方法是使用您的编程语言(perl?C#?...)匹配第一次出现,然后匹配第二次。

      编辑:我已经看到其他使用“非贪婪”运算符的响应,这可能是一个好方法,假设您的正则表达式库中有它们!

      【讨论】:

      • /a[^a]*a/ 查找接下来出现的两次“a”,但不会告诉您第二次出现在哪里。此外,它仅在基本模式正好是一个字符长时才有效。
      猜你喜欢
      • 2020-02-20
      • 1970-01-01
      • 2017-06-20
      • 2010-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多