2012-02-21 12 views
5

Moim ostatecznym celem jest zamiana następującego ciągu w JSON, ale chciałbym zadowolić się czymś, co przybliża mnie o krok, łącząc nazwę pola z każdą z wartości.Potrzebuję pomocy w dopasowaniu/zamianie Regexa

próbki danych:

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

Korzystanie Regex.Replace(), muszę go przynajmniej wyglądać następująco:

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

Ostatecznie, wynik ten będzie niesamowite, jeśli można to zrobić przez Regex w jednym wywołaniu.

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

Próbowałem wiele różnych odmian tego wzoru, ale nie wydaje się uzyskać to prawo:

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

tylko jeden przykład mogę stwierdzić, że robi coś podobnego, ale tylko wystarczająco dużo różnic, że nie mogę tego włożyć.

Regex to repeat a capture across a CDL?

EDIT: 

Oto moje rozwiązanie. Nie zamierzam opublikować tego jako "Rozwiązanie", ponieważ chcę przyznać kredyt temu, który został opublikowany przez innych. W końcu wziąłem kawałek z każdego z opublikowanych rozwiązań i wymyśliłem ten. Dziękuję wszystkim, którzy napisali. Dałem kredyt rozwiązanie, które skompilowane, wykonane najszybciej i miał najdokładniejsze wyniki.

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

Jeżeli wykonanie LINQ jest blisko Regex Jestem gra. –

+0

Byłoby interesujące porównanie na dużym łańcuchu wejściowym – sll

+0

Zgadzam się. Te same dane tutaj przedstawione są dość małe w porównaniu do tego, co próbuję przekonwertować.Obiekt rzeczywisty zawiera 31 pól i może zawierać 100-200 obiektów. –

Odpowiedz

1

miałem pojęcia, że ​​powinno być możliwe, aby to zrobić w krótszym i bardziej przejrzysty sposób. W końcu okazało się, że nie jest o wiele krótsza i można zapytać, czy jest bardziej przejrzysta. Przynajmniej jest to inny sposób rozwiązania problemu.

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

Wyjścia:

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

Dzięki za wkład. Twoje rozwiązanie skompilowane, było najszybsze (spośród innych kompilowalnych rozwiązań) i zapewniało najdokładniejsze wyniki. –

2

Now you have two problems

Nie sądzę wyrażenia regularne będzie najlepszym sposobem, aby sobie z tym poradzić. Najprawdopodobniej zacznij od podziału na średniki, a następnie przeprowadź pętlę wyników, szukając wartości zaczynającej się od "Field1:" lub "Field2:" i zbieraj wyniki w Słowniku.

traktują to jako pseudo kod, bo nie zebraliśmy lub przetestowane go:

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 ... the 'ole brute force way. Pętle. Z C# 4.0, Linq, Dynamics, Generics, etc ... musi istnieć sposób deserializacji tego przewidywalnego formatu na JSON bez pętli. –

+4

Jestem pewien, że istnieje sposób na uniknięcie pętli, ale dlaczego jest to bardziej skomplikowane? Nawet jeśli nie widzisz pętli, wciąż jest tam z Linq, itp. – mcrumley

+0

Moim celem jest to, że C# przeszło długą drogę od 1.0. Wierzę, że istnieje wzór Regex, który będzie działać, więc wolałbym iść z tym rozwiązaniem i deserialze ciąg JSON do moich obiektów. Jeśli zamierzam przechodzić przez ciąg, mogę samodzielnie budować obiekty bez deserializacji. –

1

pójdę z RegEx jak najprostszy i najbardziej bezpośredni sposób do parse struny, ale przykro mi, Nie mogłem wymyślić sprytnego, wystarczającego zastępczego sznurka, żeby zrobić to jednym strzałem.

Zrobiłem to dla zabawy, a monstrum poniżej spełnia to, czego potrzebujesz, aczkolwiek ohydnie. : -/

 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

Dobre użycie krotek w połączeniu z Linq i Lambdą. Być może będę musiał pójść z tym wyjątkiem, że jest pytanie bardzo podobne do mojego, które robi to, co chcę. Po prostu nie potrzebujesz rzeczy i nie masz nic do Splitu() na. http://stackoverflow.com/questions/2914587/regex-to-repeat-a-capture-across-a-cdl –

Powiązane problemy