2012-02-21 17 views
5

Il mio obiettivo finale qui è quello di trasformare la seguente stringa in JSON, ma mi accontenterei di qualcosa che mi fa un passo avanti combinando il nome del campo con ciascuno dei valori.Ho bisogno di aiuto su un modello di corrispondenza/sostituzione Regex

dati di esempio:

Field1:abc;def;Field2:asd;fgh; 

Utilizzando Regex.Replace(), ho bisogno di almeno simile a questa:

Field1:abc,Field1:def,Field2:asd,Field2:fgh 

In definitiva, questo risultato sarebbe impressionante se si può fare via Regex in una sola chiamata.

{"Field1":"abc","Field2":"asd"},{"Field1":"def","Field2":"fgh"} 

Ho provato molte diverse varianti di questo modello, ma non riesco a farlo bene:

(?:(\w+):)*?(?:([^:;]+);) 

Solo un altro esempio che ho trovato che sta facendo qualcosa di simile, ma solo abbastanza differenze che non riesco a mettere il dito su di esso.

Regex to repeat a capture across a CDL?

EDIT: 

Ecco la mia soluzione. Non ho intenzione di postarlo come una "Soluzione" perché voglio dare credito a uno che è stato pubblicato da altri. Alla fine, ho preso un pezzo da ognuna delle soluzioni pubblicate e ho trovato questo. Grazie a tutti quelli che hanno pubblicato. Ho dato credito alla soluzione che ha compilato, eseguito più velocemente e ha avuto i risultati più accurati.

string hbi = "Field1:aaa;bbb;ccc;ddd;Field2:111;222;333;444;"; 

    Regex re = new Regex(@"(\w+):(?:([^:;]+);)+"); 
    MatchCollection matches = re.Matches(hbi); 

    SortedDictionary<string, string> dict = new SortedDictionary<string, string>(); 

    for (int x = 0; x < matches.Count; x++) 
    { 
     Match match = matches[x]; 
     string property = match.Groups[1].Value; 

     for (int i = 0; i < match.Groups[2].Captures.Count; i++) 
     { 
      string key = i.ToString() + x.ToString(); 
      dict.Add(key, string.Format("\"{0}\":\"{1}\"", property, match.Groups[2].Captures[i].Value)); 
     } 
    } 
    Console.WriteLine(string.Join(",", dict.Values)); 
+0

Se le prestazioni di LINQ è vicino a Regex, io sono partita. –

+0

Sarebbe interessante confrontare su stringa di input grande – sll

+0

Accetto. Gli stessi dati mostrati qui sono piuttosto piccoli rispetto a quello che sto tentando di convertire.L'oggetto reale contiene 31 campi e potrebbe contenere 100-200 oggetti. –

risposta

1

Ho avuto l'idea che dovrebbe essere possibile farlo in un modo più breve e più chiaro. Alla fine non è stato molto più breve e puoi chiedertelo se è più chiaro. Almeno è un altro modo per risolvere il problema.

var str = "Field1:abc;def;Field2:asd;fgh"; 
var rows = new List<Dictionary<string, string>>(); 
int index = 0; 
string value; 
string fieldname = ""; 

foreach (var s in str.Split(';')) 
{ 
    if (s.Contains(":")) 
    { 
     index = 0; 
     var tmp = s.Split(':'); 
     fieldname = tmp[0]; 
     value = tmp[1]; 
    } 
    else 
    { 
     value = s; 
     index++; 
    } 

    if (rows.Count < (index + 1)) 
     rows.Insert(index, new Dictionary<string, string>()); 

    rows[index][fieldname] = value; 
} 

var arr = rows.Select(dict => 
        String.Join("," , dict.Select(kv => 
         String.Format("\"{0}\":\"{1}\"", kv.Key, kv.Value)))) 
        .Select(r => "{" + r + "}"); 
var json = String.Join(",", arr); 
Debug.WriteLine(json); 

Uscite:

{"Field1":"abc","Field2":"asd"},{"Field1":"def","Field2":"fgh"} 
+0

Grazie per il contributo. La tua soluzione è stata compilata, è stata la più veloce (tra le altre soluzioni compilabili) e ha fornito i risultati più accurati. –

2

Now you have two problems

Non credo che le espressioni regolari saranno il modo migliore per gestire questa situazione. Probabilmente dovresti iniziare dividendo il punto e virgola, quindi esegui il ciclo dei risultati cercando un valore che inizi con "Campo1:" o "Campo2:" e raccolga i risultati in un dizionario.

Trattare questo come pseudo codice perché non ho compilato o testato:

string[] data = input.Split(';'); 
dictionary<string, string> map = new dictionary<string, string>(); 

string currentKey = null; 
foreach (string value in data) 
{ 
    // This part should change depending on how the fields are defined. 
    // If it's a fixed set you could have an array of fields to search, 
    // or you might need to use a regular expression. 
    if (value.IndexOf("Field1:") == 0 || value.IndexOf("Field2:")) 
    { 
     string currentKey = value.Substring(0, value.IndexOf(":")); 
     value = value.Substring(currentKey.Length+1); 
    } 
    map[currentKey] = value; 
} 
// convert map to json 
+1

Yup ... il 'ole forza bruta modo. Loops. Con C# 4.0, Linq, Dynamics, Generics, ecc. Deve esistere un modo per deserializzare questo formato prevedibile in JSON senza loop. –

+4

Sono sicuro che c'è un modo per evitare i loop, ma perché renderlo più complicato? Anche se non vedi il loop, è ancora lì con Linq, ecc. – mcrumley

+0

Il mio punto è che C# ha percorso una lunga strada da 1,0. Credo che ci sia un modello Regex che funzionerà, quindi preferirei andare con quella soluzione e deserializzare la stringa JSON sui miei oggetti. Se ho intenzione di scorrere la stringa, posso costruire gli oggetti da solo senza deserializzare. –

1

vorrei andare con RegEx come il modo più semplice e diretto per parse le corde, ma mi dispiace, amico, non sono riuscito a trovare una stringa di sostituzione abbastanza intelligente per farlo in un colpo solo.

L'ho hackerato per divertimento, e la mostruosità qui sotto porta a compimento quello che ti serve, anche se orribilmente. : -/

 Regex r = new Regex(@"(?<FieldName>\w+:)*(?:(?<Value>(?:[^:;]+);)+)"); 

     var matches = r.Matches("Field1:abc;def;Field2:asd;fgh;moo;"); // Modified to test "uneven" data as well. 

     var tuples = new[] { new { FieldName = "", Value = "", Index = 0 } }.ToList(); tuples.Clear(); 

     foreach (Match match in matches) 
     { 
      var matchGroups = match.Groups; 
      var fieldName = matchGroups[1].Captures[0].Value; 
      int index = 0; 
      foreach (Capture cap in matchGroups[2].Captures) 
      { 
       var tuple = new { FieldName = fieldName, Value = cap.Value, Index = index }; 
       tuples.Add(tuple); 
       index++; 
      } 

     } 

     var maxIndex = tuples.Max(tup => tup.Index); 

     var jsonItemList = new List<string>(); 

     for (int a = 0; a < maxIndex+1; a++) 
     { 
      var jsonBuilder = new StringBuilder(); 
      jsonBuilder.Append("{"); 

      foreach (var tuple in tuples.Where(tup => tup.Index == a)) 
      { 
       jsonBuilder.Append(string.Format("\"{0}\":\"{1}\",", tuple.FieldName, tuple.Value)); 
      } 
      jsonBuilder.Remove(jsonBuilder.Length - 1, 1); // trim last comma. 
      jsonBuilder.Append("}"); 
      jsonItemList.Add(jsonBuilder.ToString()); 
     } 

     foreach (var item in jsonItemList) 
     { 
      // Write your items to your document stream. 
     } 
+0

Bel uso di tuple, combinato con Linq e Lambda. Potrei dover andare con questo tranne che c'è una domanda molto simile alla mia che fa quello che voglio. Basta non aver bisogno della roba e non avere nulla su Split() su. http://stackoverflow.com/questions/2914587/regex-to-repeat-a-capture-across-a-cdl –

Problemi correlati