替换字符串中的重叠匹配项(正则表达式或字符串操作)
Replacing overlapping matches in a string (regex or string operations)
我一直在尝试查找给定字符串中所有出现的子串,并将特定出现的地方替换为另一个子串(条件对问题不重要)。
我需要的是找到所有出现的事件(甚至是重叠的)并能够轻松替换我选择的特定事件。
问题是,如果我不使用前瞻,我将找不到重叠的事件(例如,在“aaa”中查找“aa”只会找到第一个“aa”序列,因为第二个与第一个重叠一):
var regex = new Regex(Regex.Escape("aa"));
regex.Matches("aaa").Count;
第二行的值:1
预期: 2
如果我使用 lookahead,我会找到所有出现的地方,但替换不起作用(例如,将“a”中的“a”替换为“b”,将导致“ba”而不是“b”) :
var regex = new Regex(Regex.Escape("(?=a)"));
regex.Replace("a", "b");
替换结果:ba
预期:b
当然,这些都是简单的示例,以简单的方式展示问题,但我需要它来处理任何示例。
我知道我可以轻松地搜索两者,或者手动检查这个词,但是这个代码片段会 运行 很多次,需要既高效又可读。
关于找到重叠事件同时仍然能够正确替换的任何想法/技巧?我应该使用正则表达式吗?
要获得重叠的结果,您必须将搜索模式移动一个字符,次数与搜索字符串的长度一样多。
假设包含 aaaaaa
的文本和 aaa
的 seachrstring(4 个预期匹配项),将使用搜索模式完成三个正则表达式搜索:
aaa
(2 场比赛)
(?<=a)aaa
(1 场比赛)
(?<=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 = $"{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;
}
}
我想我会放弃正则表达式并编写一个简单的循环如下(有改进的余地),因为我认为它会更快更容易理解。
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");
我一直在尝试查找给定字符串中所有出现的子串,并将特定出现的地方替换为另一个子串(条件对问题不重要)。 我需要的是找到所有出现的事件(甚至是重叠的)并能够轻松替换我选择的特定事件。
问题是,如果我不使用前瞻,我将找不到重叠的事件(例如,在“aaa”中查找“aa”只会找到第一个“aa”序列,因为第二个与第一个重叠一):
var regex = new Regex(Regex.Escape("aa"));
regex.Matches("aaa").Count;
第二行的值:1 预期: 2
如果我使用 lookahead,我会找到所有出现的地方,但替换不起作用(例如,将“a”中的“a”替换为“b”,将导致“ba”而不是“b”) :
var regex = new Regex(Regex.Escape("(?=a)"));
regex.Replace("a", "b");
替换结果:ba 预期:b
当然,这些都是简单的示例,以简单的方式展示问题,但我需要它来处理任何示例。 我知道我可以轻松地搜索两者,或者手动检查这个词,但是这个代码片段会 运行 很多次,需要既高效又可读。
关于找到重叠事件同时仍然能够正确替换的任何想法/技巧?我应该使用正则表达式吗?
要获得重叠的结果,您必须将搜索模式移动一个字符,次数与搜索字符串的长度一样多。
假设包含 aaaaaa
的文本和 aaa
的 seachrstring(4 个预期匹配项),将使用搜索模式完成三个正则表达式搜索:
aaa
(2 场比赛)(?<=a)aaa
(1 场比赛)(?<=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 = $"{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;
}
}
我想我会放弃正则表达式并编写一个简单的循环如下(有改进的余地),因为我认为它会更快更容易理解。
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");