Domanda

devo leggere in un file che contiene una serie di coordinate. Il file è strutturato nel seguente modo:

X1/Y1,X2/Y2,X3/Y3,X4/Y4

dove X e Y sono numeri interi positivi. Per risolvere questo problema che voglio usare una regex (credo che questo sia, in generale, una buona idea a causa della minima refactoring quando i cambiamenti del modello).

Per questo ho sviluppato la seguente espressione regolare:

Regex r = new Regex(@^(?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+))(,(?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+)))*$");

Tuttavia quando prova questo regex sui dati, ad esempio:

1302/1425,1917/2010

L'Regex sembra solo ricordare l'ultimo X, Y e di gruppo Coor. In questo caso Coor è "12/17", X è "1917" e Y è "2010". C'è un modo per generare una sorta di albero. Così ho trovato un oggetto che mi dà tutte le espressioni Coor, con sotto ogni Coor una X e Y componente?

Se possibile, vorrei utilizzare un solo Regex, questo perché il formato potrebbe forse passare ad un altro.

È stato utile?

Soluzione

Si può facilmente risolvere questo senza alcuna espressione regolare utilizzando string.Split e int.Parse:

var coords = s.Split(',')
    .Select(x => x.Split('/'))
    .Select(a => new {
        X = int.Parse(a[0]),
        Y = int.Parse(a[1])
    });

Se si vuole usare un'espressione regolare per validate la stringa che si possa fare in questo modo:

"^(?!,)(?:(?:^|,)[0-9]+/[0-9]+)*$"

Se si desidera utilizzare un metodo ordinario basata espressione anche per estrazione dei dati si potrebbe prima convalidare la stringa utilizzando l'espressione regolare sopra e poi in più i dati nel seguente modo:

var coords = Regex.Matches(s, "([0-9]+)/([0-9]+)")
    .Cast<Match>()
    .Select(match => new
    {
        X = int.Parse(match.Groups[1].Value),
        Y = int.Parse(match.Groups[2].Value)
    });

Se si vuole veramente per eseguire l'estrazione e la convalida dei dati simultaneamente con una sola espressione regolare è possibile utilizzare due gruppi di cattura e trovare i risultati nella proprietà Captures per ogni gruppo. Ecco un modo è possibile eseguire sia la convalida e l'estrazione dei dati utilizzando una singola espressione regolare:

List<Group> groups =
    Regex.Matches(s, "^(?!,)(?:(?:^|,)([0-9]+)/([0-9]+))*$")
         .Cast<Match>().First()
         .Groups.Cast<Group>().Skip(1)
         .ToList();

var coords = Enumerable.Range(0, groups[0].Captures.Count)
    .Select(i => new
    {
        X = int.Parse(groups[0].Captures[i]),
        Y = int.Parse(groups[1].Captures[i])
    });

Tuttavia si può prendere in considerazione se la complessità di questa soluzione è la pena rispetto alla soluzione basata string.Split.

Altri suggerimenti

Non v'è alcun motivo per usare un'espressione regolare per un formato così semplice.

Basta dividere la stringa e utilizzare operazioni sulle stringhe semplici per ottenere le coordinate:

var coordinates =
  fileContent.Split(',').Select(s => {
    int pos = s.IndexOf("/");
    return new {
      X = s.Substring(0, pos),
      Y = s.Substring(pos + 1)
    };
  });

Se il formato di file diventa molto più complicato è possibile refactoring in usando un'espressione regolare. Fino ad allora, semplice codice come questo è molto più facile da mantenere.

Si potrebbe ottenere ciò che cercate, se si utilizza il "Partite", piuttosto che il comando "Match". Inoltre, non si può ridurre l'espressione regolare forse per questo:

Regex(@"((?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+))|,)*");

Credo che il vostro primo problema è che il regex è viziata, le ancore stanno gettando fuori l'abbinamento. Questo è quello che mi si avvicinò con: (solo la regex mostrato qui, nessun codice)

(?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+))

Quello mistagogo funziona pure, ma produce 'vuoto' partite sulle virgole (per me).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top