首页 > 解决方案 > 替换字符串中的重叠匹配项(正则表达式或字符串操作)

问题描述

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

问题是,如果我不使用前瞻,我将找不到重叠的事件(例如,在“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

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

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

标签: c#regexstringreplaceregex-lookarounds

解决方案


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

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

  • aaa(2 场比赛)
  • (?<=a)aaa(1 场比赛)
  • (?<=aa)aaa(1 场比赛)

同样适用于更复杂的搜索,例如abain abababa

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 = $"{lookahead}{searchstring}";
    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;
    }
}

推荐阅读