2013-03-02 13 views
8

mi stosując poniższe wyrażenieRegex z nie przechwytywania grupę C#

JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)* 

w poniższej typ danych

JOINTS    DISPL.-X    DISPL.-Y    ROTATION 


    1   0.000000E+00   0.975415E+01   0.616921E+01 
    2   0.000000E+00   0.000000E+00   0.000000E+00 

Chodzi o to, aby wyodrębnić na dwie grupy, z których każda zawierała linię (począwszy od numeru złącza, 1, 2 itd.) Kod C# wygląda następująco:

string jointPattern = @"JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)*"; 
MatchCollection mc = Regex.Matches(outFileSection, jointPattern); 
foreach (Capture c in mc[0].Captures) 
{ 
    JointOutput j = new JointOutput(); 
    string[] vals = c.Value.Split(); 
    j.Joint = int.Parse(vals[0]) - 1; 
    j.XDisplacement = float.Parse(vals[1]); 
    j.YDisplacement = float.Parse(vals[2]); 
    j.Rotation = float.Parse(vals[3]); 
    joints.Add(j); 
} 

To jednak nie działa: raczej niż zwracając dwie przechwycone grupy (grupa wewnętrzna), zwraca jedną grupę: cały blok, łącznie z nagłówkami kolumn. Dlaczego to się dzieje? Czy C# radzi sobie inaczej z niezapisanymi grupami?

Są wreszcie najlepsze sposoby na zrobienie tego? (Naprawdę czuję, że mam teraz dwa problemy.)

Odpowiedz

8

mc[0].Captures odpowiada mc[0].Groups[0].Captures. Groups[0] zawsze odnosi się do całego dopasowania, więc zawsze będzie tylko jedno przechwycone skojarzenie z nim. Część, której szukasz, jest przechwytywana w grupie nr 1, więc powinieneś używać mc[0].Groups[1].Captures.

Ale twoje wyrażenie regularne jest zaprojektowane tak, aby dopasować całe wejście za jednym zamachem, więc metoda Matches() zawsze zwróci MatchCollection z tylko jednym Matchem (zakładając, że mecz się powiódł). Równie dobrze można użyć Match() zamiast:

Match m = Regex.Match(source, jointPattern); 
    if (m.Success) 
    { 
    foreach (Capture c in m.Groups[1].Captures) 
    { 
     Console.WriteLine(c.Value); 
    } 
    } 

wyjściowa:

1   0.000000E+00   0.975415E+01   0.616921E+01 
2   0.000000E+00   0.000000E+00   0.000000E+00 
+0

Wiesz, rzeczywiście sprawdziłem MSDN, aby dowiedzieć się, jak działa własność 'Captures' (nigdy sam jej nie używałem) i nie zauważyłem, że odnosi się ona do grupy 0 (która jest oczywiście główną przyczyną konsternacji dla OP). +1! – Cameron

1

Występują dwa problemy: powtarzająca się część (?:...) nie pasuje poprawnie; i .* jest chciwy i pochłania wszystkie dane wejściowe, więc powtarzająca się część nigdy nie pasuje, nawet gdyby mogła.

Użyj tego zamiast:

JOINTS.*?[\r\n]+(?:\s*(\d+\s*\S*\s*\S*\s*\S*)[\r\n\s]*)* 

ten ma zakaz chciwy wiodącą rolę, zapewnia, że ​​część linii dopasowywania rozpoczyna się w nowej linii (nie w środku tytule) i wykorzystuje [\r\n\s]* w przypadek, że nowe znaki nie są dokładnie takie, jakich oczekujesz.

Osobiście użyłbym do tego celu wyrażenia regularnego, ale lubię wyrażenia regularne :-) Jeśli zdajesz sobie sprawę, że struktura ciągu znaków zawsze będzie [title] [nowa linia] [nowa linia] [linie], to może jest bardziej proste (jeśli mniej elastyczne), aby podzielić się na nowe linie i odpowiednio je przetwarzać.

Wreszcie, możesz użyć regex101.com lub jednej z wielu innych stron testujących regex, aby pomóc w debugowaniu swoich wyrażeń regularnych.

+0

Nie, nadal nie działa.Daje jedną dużą grupę przechwytywania zawierającą wszystko od JOINTS aż do ostatniego numeru zmiennoprzecinkowego. – ian93

+0

@ ian93: Spróbuj teraz, naprawiłem obsługę nowej linii/rozpoczęcie obsługi linii. Poza tym, dlaczego używasz 'Meczów', jeśli wiesz, że będzie tylko jeden mecz? – Cameron

+0

@Cameron będzie pasował tylko do pierwszego, dodaj '*' lub '+' na końcu – tttony

2

Chciałbym po prostu nie używać Regex do podnoszenia ciężkich i analizowania tekstu.

var data = @"  JOINTS    DISPL.-X    DISPL.-Y    ROTATION 


     1   0.000000E+00   0.975415E+01   0.616921E+01 
     2   0.000000E+00   0.000000E+00   0.000000E+00"; 

var lines = data.Split('\r', '\n').Where(s => !string.IsNullOrWhiteSpace(s)); 
var regex = new Regex(@"(\S+)"); 

var dataItems = lines.Select(s => regex.Matches(s)).Select(m => m.Cast<Match>().Select(c => c.Value)); 

enter image description here

+0

Myślę, że mogę iść z tym podejściem, ale używając mniej Linq, ponieważ muszę zachować kod i patrzeć na ciebie, nie mam pojęcia, co się dzieje ... – ian93

+3

Może chcesz nauczyć się Linq, ponieważ to naprawdę potężne. Ostatnie linie pasują do każdej linii i wyciągają wszystko, co nie jest spacją, a następnie wyodrębniają wartości z 'CaptureCollection', które znajdują się wewnątrz' MatchCollection'. – Romoku

+5

Przyznaję, to trochę zabawne, gdy facet używający instrukcji regex odrzuca instrukcję linq, by wyglądać na skomplikowaną. W zwięzłości jest moc, dotyczy to zarówno linq, jak i wyrażeń regularnych. –

1

Dlaczego nie po prostu uchwycić wartości i zignorować resztę. Oto wyrażenie, które pobiera wartości.

string data = @"JOINTS DISPL.-X DISPL.-Y ROTATION 
1 0.000000E+00 0.975415E+01 0.616921E+01 
2 0.000000E+00 0.000000E+00 0.000000E+00"; 

string pattern = @"^ 
\s+ 
(?<Joint>\d+) 
\s+ 
(?<ValX>[^\s]+) 
\s+ 
(?<ValY>[^\s]+) 
\s+ 
(?<Rotation>[^\s]+)"; 

var result = Regex.Matches(data, pattern, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture) 
        .OfType<Match>() 
        .Select (mt => new 
        { 
        Joint = mt.Groups["Joint"].Value, 
        ValX = mt.Groups["ValX"].Value, 
        ValY = mt.Groups["ValY"].Value, 
        Rotation = mt.Groups["Rotation"].Value, 
        }); 
/* result is 
IEnumerable<> (2 items) 
Joint ValX ValY Rotation 
1 0.000000E+00 0.975415E+01 0.616921E+01 
2 0.000000E+00 0.000000E+00 0.000000E+00 
*/ 
Powiązane problemy