如何让正则表达式匹配只添加一次到匹配集合中?
Posted
技术标签:
【中文标题】如何让正则表达式匹配只添加一次到匹配集合中?【英文标题】:How can I get a regex match to only be added once to the matches collection? 【发布时间】:2010-10-14 13:17:17 【问题描述】:我有一个字符串,其中有几个 html cmets。我需要计算一个表达式的唯一匹配项。
例如,字符串可能是:
var teststring = "<!--X1-->Hi<!--X1-->there<!--X2-->";
我目前使用它来获取匹配项:
var regex = new Regex("<!--X.-->");
var matches = regex.Matches(teststring);
这个结果是 3 场比赛。但是,我希望只有 2 个匹配项,因为只有两个唯一匹配项。
我知道我可能可以遍历生成的 MatchCollection 并删除额外的匹配,但我希望有一个更优雅的解决方案。
澄清:示例字符串与实际使用的字符串相比已大大简化。很容易有一个 X8 或 X9,并且每个字符串中可能有几十个。
【问题讨论】:
【参考方案1】:我会像这样使用Enumerable.Distinct Method:
string subjectString = "<!--X1-->Hi<!--X1-->there<!--X2--><!--X1-->Hi<!--X1-->there<!--X2-->";
var regex = new Regex(@"<!--X\d-->");
var matches = regex.Matches(subjectString);
var uniqueMatches = matches
.OfType<Match>()
.Select(m => m.Value)
.Distinct();
uniqueMatches.ToList().ForEach(Console.WriteLine);
输出这个:
<!--X1-->
<!--X2-->
对于正则表达式,你可以使用这个吗?
(<!--X\d-->)(?!.*\1.*)
似乎至少可以在 RegexBuddy 中处理您的测试字符串 =)
// (<!--X\d-->)(?!.*\1.*)
//
// Options: dot matches newline
//
// Match the regular expression below and capture its match into backreference number 1 «(<!--X\d-->)»
// Match the characters “<!--X” literally «<!--X»
// Match a single digit 0..9 «\d»
// Match the characters “-->” literally «-->»
// Assert that it is impossible to match the regex below starting at this position (negative lookahead) «(?!.*\1.*)»
// Match any single character «.*»
// Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
// Match the same text as most recently matched by capturing group number 1 «\1»
// Match any single character «.*»
// Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
【讨论】:
我喜欢这个想法,但不幸的是结果不是预期的。在我的单元测试(它有更大的字符串)中,当我应该收到 4 个结果时,我得到了 8 个结果。不确定 RegexBuddy 和我正在使用的有什么区别。 :( 另外,我尝试使用 Distinct() 但 MatchCollection,即使它源自 IEnumerable,似乎也无法识别这一点。 你更大的字符串是多少?在 MatchCollection 上,您很可能必须使用 var stuff = theMatchCollection.OfType看来你在做两件不同的事情:
-
匹配 cmets,如 //
寻找唯一的 cmets 集
因此将这些作为两个不同的步骤处理是相当合乎逻辑的:
var regex = new Regex("<!--X.-->");
var matches = regex.Matches(teststring);
var uniqueMatches = matches.Cast<Match>().Distinct(new MatchComparer());
class MatchComparer : IEqualityComparer<Match>
public bool Equals(Match a, Match b)
return a.Value == b.Value;
public int GetHashCode(Match match)
return match.Value.GetHashCode();
【讨论】:
你测试过这个吗?出于某种原因,即使这是包含它的第二个答案,我也无法让 Distinct() 与 MatchCollection 一起使用。我正在使用 .NET3.5 并且在我的 using 语句中有 System.Linq。 你应该使用 OfType 而不是 Cast 我想确保它抛出异常,如果除了匹配之外的任何东西出现。 OfType 将继续并忽略可能不是 Match 的内容,这可能会隐藏潜在的问题。【参考方案3】:提取 cmets 并将它们存储在一个数组中。然后您可以过滤掉唯一值。
但我不知道如何在 C# 中实现这一点。
【讨论】:
【参考方案4】:取决于你有多少个 Xn 你可以使用:
(\<!--X1--\>)1.*(\<!--X2--\>)1
这只会匹配每次出现的 X1、X2 等,前提是它们是按顺序排列的。
【讨论】:
【参考方案5】:将评论的内部部分捕获为一个组。然后将这些字符串放入哈希表(字典)中。然后向字典询问它的计数,因为它会自我清除重复。
var teststring = "<!--X1-->Hi<!--X1-->there<!--X2-->";
var tokens = new Dicationary<string, string>();
Regex.Replace(teststring, @"<!--(.*)-->",
match =>
tokens[match.Groups[1].Value] = match.Groups[1].Valuel;
return "";
);
var uniques = tokens.Keys.Count;
通过使用 Regex.Replace 构造,您可以在每次匹配时调用一个 lambda。由于您对替换不感兴趣,因此不要将其设置为等于任何内容。
您必须使用 Group[1],因为 group[0] 是整个匹配项。 我只是在两边重复同样的事情,这样更容易放入只存储唯一键的字典中。
【讨论】:
【参考方案6】:如果你想从 MatchCollection 中得到一个不同的匹配列表而不转换为字符串,你可以使用这样的东西:
var distinctMatches = matchList.OfType<Match>().GroupBy(x => x.Value).Select(x =>x.First()).ToList();
我知道已经 12 年了,但有时我们需要这种解决方案,所以我想分享一下。 C# 进化了,.NET 进化了,所以现在更容易了。
【讨论】:
以上是关于如何让正则表达式匹配只添加一次到匹配集合中?的主要内容,如果未能解决你的问题,请参考以下文章