2013-03-02 10 views
8

Sto usando la seguente espressione regolareRegex con il gruppo non-cattura in C#

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

sui seguenti tipi di dati:

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 

L'idea è quella di estrarre due gruppi, ognuno contenente una linea (a partire dal numero comune, 1, 2, ecc) il codice C# è la seguente:

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

Tuttavia, questo non funziona: invece di restituendo due gruppi catturati (il gruppo interno), restituisce un gruppo: l'intero blocco, incluse le intestazioni delle colonne. Perché succede? C# tratta in modo diverso i gruppi non catturati?

Infine, le RegEx sono il modo migliore per farlo? (Mi sento davvero come se avessi due problemi ora.)

risposta

8

mc[0].Captures è equivalente a mc[0].Groups[0].Captures. Groups[0] si riferisce sempre all'intera partita, quindi ci sarà sempre e solo la cattura associata ad essa. La parte che stai cercando viene catturata nel gruppo n. 1, quindi dovresti utilizzare mc[0].Groups[1].Captures.

Ma la tua espressione regolare è progettata per corrispondere all'intero input in un tentativo, quindi il metodo Matches() restituirà sempre un MatchCollection con una sola corrispondenza in esso (supponendo che la corrispondenza abbia esito positivo). Si potrebbe anche usare Match() invece:

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

uscita:

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

Sai, ho effettivamente controllato MSDN per scoprire come funziona la proprietà 'Capture' (non l'ho mai usata io stesso), e non ho notato che si riferisce al gruppo 0 (che è ovviamente la causa principale di costernazione per il PO). +1! – Cameron

1

Ci sono due problemi: la parte ripetitiva (?:...) non corrisponde correttamente; e il .* è avido e consuma tutto l'input, quindi la parte ripetitiva non corrisponde mai anche se potesse.

Utilizzare questo invece:

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

Questo ha un ruolo di primo piano non avido, assicura che la parte di linea corrispondente inizia su una nuova linea (non nel bel mezzo di un titolo), e usa [\r\n\s]* in caso le newline non sono esattamente come ti aspetti.

Personalmente, vorrei usare espressioni regex per questo, ma mi piace regex :-) Se ti capita di sapere che la struttura della stringa sarà sempre [titolo] [newline] [newline] [linee] allora forse è più semplice (se meno flessibile) per dividere semplicemente su newline e procedere di conseguenza.

Infine, è possibile utilizzare regex101.com o uno dei numerosi altri siti di test di espressioni regolari per eseguire il debug delle espressioni regolari.

+0

No, ancora non funziona.Fornisce un grande gruppo di cattura contenente tutto, dai GIUNTI fino all'ultimo numero in virgola mobile. – ian93

+0

@ ian93: Provalo ora, ho corretto la gestione della nuova riga/l'inizio della gestione della linea. Inoltre, perché stai usando "Partite" se sai che ci sarà una sola partita? – Cameron

+0

@Cameron corrisponderà solo al primo, aggiungere '*' o '+' alla fine – tttony

2

Non vorrei utilizzare Regex per il sollevamento pesante e analizzare il testo.

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

Penso che potrei andare con questo approccio, ma usando meno Linq, dato che devo mantenere il codice e guardando il tuo non ho idea di cosa stia succedendo ... – ian93

+3

Potrebbe voler imparare Linq perché è davvero potente. Le ultime righe corrispondono a ciascuna riga e estrae tutto ciò che non è uno spazio e quindi estraggono i valori da 'CaptureCollection' all'interno di' MatchCollection'. – Romoku

+5

Devo ammettere che è piuttosto divertente quando un ragazzo che usa le espressioni regex respinge un'istruzione linq per sembrare complessa. C'è del potere nell'essere succinti, si applica a linq e alle regex. –

1

Perché non solo catturare i valori e ignorare il resto. Ecco una regex che ottiene i valori.

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 
*/