2009-03-20 12 views
13

Mam ciąg znaków zawierający kilka komentarzy HTML. Muszę liczyć unikalne dopasowania wyrażenia.Jak uzyskać dopasowanie wyrażenia regularnego, które zostanie dodane tylko raz do kolekcji dopasowań?

Na przykład ciąg może być:

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

Obecnie używać tego, aby uzyskać mecze:

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

Wyniki to 3 mecze. Chciałbym jednak, aby były to tylko 2 mecze, ponieważ są tylko dwa unikalne mecze.

Wiem, że mogę przepuścić przez wynikową MatchCollection i usunąć dodatkowe dopasowanie, ale mam nadzieję, że istnieje bardziej eleganckie rozwiązanie.

Klasyfikacja: Przykładowy ciąg znaków jest znacznie uproszczony w stosunku do aktualnie używanego. Nie może być łatwo X8 lub X9, i prawdopodobnie są dziesiątki każdego w ciągu.

Odpowiedz

22

Chciałbym po prostu użyć Enumerable.Distinct Method na przykład tak:

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

Wyjścia to:

<!--X1--> 
<!--X2--> 

Dla wyrażenia regularnego, można może wykorzystać ten jeden?

(<!--X\d-->)(?!.*\1.*) 

wydaje się działać na ciąg testowym w RegexBuddy przynajmniej =)

// (<!--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) «*» 
+0

Podobał mi się ten pomysł, ale niestety wyniki nie są zgodne z oczekiwaniami. W moim teście jednostkowym (który ma znacznie większy ciąg) uzyskałem 8 wyników, gdy powinienem był otrzymać 4. Nie jestem pewien jaka jest różnica między RegexBuddy a tym, czego używam. :( –

+0

Próbowałem również używać Distinct(), ale MatchCollection, mimo że pochodzi od IEnumerable, nie wydaje się rozpoznawać tego. –

+0

Jaki jest twój znacznie większy ciąg? W MatchCollection najprawdopodobniej będziesz musiał użyć var ​​stuff = theMatchCollection.OfType (). Wybierz (m => m.Value) .Distinct() lub coś takiego – Svish

0

Wyodrębnij komentarze i umieść je w tablicy. Następnie możesz odfiltrować unikalne wartości.

Ale nie wiem, jak to zaimplementować w języku C#.

0

wychwytywania wewnętrzna część komentarza jako grupa. Następnie umieść te struny w hashtable (słowniku). Następnie zapytaj słownik o jego licznik, ponieważ sam powtarza się.

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; 

Korzystając z konstrukcji Regex.Replace uzyskuje się wywołanie lambda w każdym meczu. Ponieważ nie jesteś zainteresowany wymianą, nie ustawiasz jej na równi z niczym.

Musisz użyć grupy [1], ponieważ grupa [0] jest cała zgodna. Powtarzam to samo po obu stronach, dzięki czemu łatwiej jest umieścić je w słowniku, w którym przechowywane są tylko unikatowe klucze.

0

W zależności od tego, ile masz na Xn może być w stanie używać:

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

który będzie pasował tylko każde wystąpienie X1, X2 itp raz pod warunkiem, że są w porządku.

2

Wydaje robisz dwie różne rzeczy:

  1. Dopasowany komentarze typu/< - X ->/
  2. Znalezienie zestaw unikatowych komentarzach

więc jest dość logiczne, aby obsłużyć je jako dwa różne kroki:

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

Testowałeś to? Z jakiegoś powodu nie mogę uzyskać funkcji Distinct() do pracy z MatchCollection, mimo że jest to druga odpowiedź, która go zawiera. Używam .NET3.5 i mam System.Linq w moich instrukcjach używania. –

+0

Naprawiono kod tak, aby działał. – user7116

+0

powinieneś używać OfType, a nie Cast – Svish

Powiązane problemy