替换字符串中的重叠匹配项(正则表达式或字符串操作)

Posted

技术标签:

【中文标题】替换字符串中的重叠匹配项(正则表达式或字符串操作)【英文标题】:Replacing overlapping matches in a string (regex or string operations) 【发布时间】:2021-08-02 12:33:24 【问题描述】:

我一直在尝试查找给定字符串中所有出现的子字符串,并用另一个子字符串替换特定的出现(条件对于问题并不重要)。 我需要的是找到所有出现(甚至是重叠的)并能够轻松替换我选择的特定。

问题是,如果我不使用前瞻,我无法找到重叠的出现(例如,在“aaa”中查找“aa”只会找到第一个“aa”序列,因为第二个与第一个重叠) :

var regex = new Regex(Regex.Escape("aa"));
regex.Matches("aaa").Count;

第二行的值: 1 预计: 2

如果我使用前瞻,我会找到所有的匹配项,但替换不起作用(例如,将“a”中的“a”替换为“b”,将导致“ba”而不是“b”):

var regex = new Regex(Regex.Escape("(?=a)"));
regex.Replace("a", "b");

替换结果: ba 预期: b

当然,这些都是简单的示例,以简单的方式展示问题,但我需要它来处理任何示例。 我知道我可以轻松地对两者进行搜索,或者手动检查单词,但是这段代码 sn-p 会运行很多次,并且需要高效且可读。

关于查找重叠事件同时仍能正确替换的任何想法/提示?我什至应该使用正则表达式吗?

【问题讨论】:

你能提供一些更简单的例子吗?如果您的输入是“aaa”,并且您想将“aa”替换为“b”,结果应该是什么样的? 取决于我要替换的事件。这是我在幕后做的事情,以确保正则表达式替换正确的出现,但我找不到所有的出现,这是我的问题......选择正确出现的代码很长,它不会依赖于正则表达式本身,它只需要访问所有可能出现的情况 hm 我不确定是否可以重叠,但您可以搜索 aa(?<=a)aa 并组合结果。 问题在于我不会只有一个“a”序列,我可能会有更复杂的子字符串 在这种情况下我会放弃使用正则表达式并使用子字符串编写一个简单的循环来查找所有出现的地方。 【参考方案1】:

我想我会放弃正则表达式并编写一个简单的循环如下(还有改进的空间),因为我认为它会更快,更容易理解。

        public IEnumerable<int> FindStartingOccurrences(string input, string pattern)
        
            var occurrences = new List<int>();

            for (int i=0; i<input.Length; i++)
            
                if (input.Length+1 > i+pattern.Length)
                
                    if (input.Substring(i, pattern.Length) == pattern)
                    
                        occurrences.Add(i);
                    
                
            

            return occurrences;
        

然后像这样调用:

var occurrences = FindStartingOccurrences("aaabbaaaaaccaadaaa", "aa");

【讨论】:

我会尝试这两种解决方案(或至少是适合我的代码的变体)并选择在这种情况下更有帮助的答案(我需要测试性能以确保) .非常感谢您的回答! 我尝试了多种不同的解决方案: 1. 多种正则表达式方法 2. Moore-Bayer 算法 3. 这个 而您的解决方案与生成器相结合,比其他解决方案快很多(大约 10 倍平均速度更快!)出于我的目的。谢谢:)【参考方案2】:

要获得重叠的结果,您必须将搜索模式移动一个字符,次数与搜索字符串的长度一样多。

假设对于包含aaaaaaaaa 的搜索字符串(4 个预期匹配)的文本,将使用搜索模式完成三个正则表达式搜索:

aaa(2 场比赛) (?&lt;=a)aaa(1 场比赛) (?&lt;=aa)aaa(1 场比赛)

同样适用于更复杂的搜索,如 abababa 中的 aba

private static IEnumerable<Match> GetOverlappingMatches(string text, string searchstring)

    IEnumerable<Match> combinedMatches = Enumerable.Empty<Match>();

    for (int i = 0; i < searchstring.Length; i++)
    
        combinedMatches = combinedMatches.Concat(GetMatches(text, searchstring, i));
    

    return combinedMatches.Distinct(new MatchComparer());


private static IEnumerable<Match> GetMatches(string text, string searchstring, int shifts)

    string lookahead = $"(?<=searchstring.Substring(0, shifts))";
    string pattern = $"lookaheadsearchstring";
    return Regex.Matches(text, pattern);

您还想添加一个MatchComparer 来过滤双重匹配。

public class MatchComparer : IEqualityComparer<Match>

    public bool Equals(Match x, Match y)
    
        return x.Index == y.Index
            && x.Length == y.Length;
    

    public int GetHashCode([DisallowNull] Match obj)
    
        return obj.Index ^ obj.Length;
    

【讨论】:

这很好,但我仍然认为一个简单的循环会更快,更有意义。 非常有趣且写得很好,这个解决方案看起来很优雅,但可能真的很贵。将测试性能并通知您。非常感谢!

以上是关于替换字符串中的重叠匹配项(正则表达式或字符串操作)的主要内容,如果未能解决你的问题,请参考以下文章

C ++正则表达式替换第一个匹配项

如何将重叠字符串与正则表达式匹配?

powershell中的正则表达式

在 C# 中获取重叠的正则表达式匹配

根据匹配用替换字符串替换字符串中的正则表达式模式

如何检测两个正则表达式在它们可以匹配的字符串中是不是重叠?