Как я могу получить регулярное выражение только для добавления в коллекцию матчей?

У меня есть строка, в которой есть несколько html-комментариев. Мне нужно подсчитать уникальные соответствия выражения.

Например, строка может быть:

var teststring = "<!--X1-->Hi<!--X1-->there<!--X2-->";

В настоящее время я использую это для получения совпадений:

var regex = new Regex("<!--X.-->");
var matches = regex.Matches(teststring);

Результат этого - 3 совпадения. Тем не менее, я хотел бы, чтобы это было всего 2 матча, так как есть только два уникальных матча.

Я знаю, что могу, возможно, пропустить полученный MatchCollection и удалить дополнительный Match, но я надеюсь, что есть более элегантное решение.

Разъяснение: строка примера значительно упрощена из того, что на самом деле используется. Легко может быть 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) «*»

Ответ 2

Кажется, вы делаете две разные вещи:

  • Соответствующие комментарии, такие как /< - X. → /
  • Поиск набора уникальных комментариев

Поэтому вполне логично обрабатывать их как два разных шага:

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();
    }
}

Ответ 3

Извлеките комментарии и сохраните их в массиве. Затем вы можете отфильтровать уникальные значения.

Но я не знаю, как реализовать это на С#.

Ответ 4

Захватите внутреннюю часть комментария как группу. Затем поместите эти строки в хэш-таблицу (словарь). Затем спросите словарь для его подсчета, так как он будет повторять повторные повторы.

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], потому что группа [0] - это полное совпадение. Я повторяю одно и то же с обеих сторон, так что его легче вставить в словарь, в котором хранятся только уникальные ключи.

Ответ 5

В зависимости от того, сколько у вас Xn вы могли бы использовать:

(\<!--X1--\>){1}.*(\<!--X2--\>){1}

Это будет соответствовать только каждому вхождению X1, X2 и т.д., если они приведены в порядок.