2009-06-22 12 views
7

W programie czytam w niektórych plikach danych, których część jest sformatowana jako seria rekordów w nawiasach kwadratowych. Każdy rekord zawiera tytuł sekcji i serię par klucz/wartość.Czy możesz poprawić ten kod wyrażeń regularnych C#?

Pierwotnie napisałem kod do pętli i wyodrębnienie wartości, ale zdecydowałem, że można to zrobić bardziej elegancko za pomocą wyrażeń regularnych. Poniżej znajduje się mój wynikowy kod (właśnie go zhakowałem na razie w aplikacji konsolowej - więc wiem, że nazwy zmiennych nie są zbyt dobre, itp.)

Czy możesz zaproponować ulepszenia? Uważam, że nie powinno to być konieczne dwa mecze i podciąg, ale nie można dowiedzieć się, jak zrobić to wszystko w jednym dużym kroku:

string input = "[section1 key1=value1 key2=value2][section2 key1=value1 key2=value2 key3=value3][section3 key1=value1]"; 

MatchCollection matches=Regex.Matches(input, @"\[[^\]]*\]"); 
foreach (Match match in matches) 
{ 
    string subinput = match.Value; 

    int firstSpace = subinput.IndexOf(' '); 
    string section = subinput.Substring(1, firstSpace-1); 
    Console.WriteLine(section); 

    MatchCollection newMatches = Regex.Matches(subinput.Substring(firstSpace + 1), @"\s*(\w+)\s*=\s*(\w+)\s*"); 
    foreach (Match newMatch in newMatches) 
    { 
     Console.WriteLine("{0}={1}", newMatch.Groups[1].Value, newMatch.Groups[2].Value); 
    } 
} 

Odpowiedz

7

Wolę nazwanych przechwytuje, ładne formatowanie, i jasność:

string input = "[section1 key1=value1 key2=value2][section2 key1=value1 key2=value2 key3=value3][section3 key1=value1]"; 
MatchCollection matches = Regex.Matches(input, @"\[ 
                (?<sectionName>\S+) 
                 (\s+                
                 (?<key>[^=]+) 
                  = 
                 (?<value>[^ \] ]+)              
                )+ 
                ]", RegexOptions.IgnorePatternWhitespace); 

foreach(Match currentMatch in matches) 
{ 
    Console.WriteLine("Section: {0}", currentMatch.Groups["sectionName"].Value); 
    CaptureCollection keys = currentMatch.Groups["key"].Captures; 
    CaptureCollection values = currentMatch.Groups["value"].Captures; 

    for(int i = 0; i < keys.Count; i++) 
    { 
     Console.WriteLine("{0}={1}", keys[i].Value, values[i].Value);   
    } 
} 
+0

Miło, nie wiedziałem o użyciu opcji IgnorePatternWhitespace, aby umożliwić sformatowanie takiego wyrażenia regularnego. Dzięki za wskazówkę. –

+0

+1 dla RegexOptions.IgnorePatternWhitespace tak dla czytelności –

+0

+1 Ja też wolę nazwane przechwytywania. Sprawiają, że kod jest czytelny i łatwy do zrozumienia. –

2

Powinieneś być w stanie coś zrobić z grup zagnieżdżonych tak:

pattern = @"\[(\S+)(\s+([^\s=]+)=([^\s\]]+))*\]" 

i nie przetestowałem go w C# lub zapętlono przez mecze, ale wyniki wyglądają dobrze na rubular.com

+0

+1 za link. –

5

Powinieneś skorzystać z kolekcji, aby zdobyć każdy klucz. Więc coś takiego następnie: wyjście

 string input = "[section1 key1=value1 key2=value2][section2 key1=value1 key2=value2 key3=value3][section3 key1=value1]"; 

     Regex r = new Regex(@"(\[(\S+) (\s*\w+\s*=\s*\w+\s*)*\])", RegexOptions.Compiled); 

     foreach (Match m in r.Matches(input)) 
     { 
      Console.WriteLine(m.Groups[2].Value); 
      foreach (Capture c in m.Groups[3].Captures) 
      { 
       Console.WriteLine(c.Value); 
      } 
     } 

Wynikające:

section1 
key1=value1 
key2=value2 
section2 
key1=value1 
key2=value2 
key3=value3 
section3 
key1=value1 
-1

Pozwoli to dopasować wszystkie pary klucz/wartość ...

var input = "[section1 key1=value1 key2=value2][section2 key1=value1 key2=value2 key3=value3][section3 key1=value1]"; 

var ms = Regex.Matches(input, @"section(\d+)\s*(\w+=\w+)\s*(\w+=\w+)*"); 

foreach (Match m in ms) 
{ 
    Console.WriteLine("Section " + m.Groups[1].Value); 

    for (var i = 2; i < m.Groups.Count; i++) 
    { 
     if(!m.Groups[i].Success) continue; 
     var kvp = m.Groups[i].Value.Split('='); 
     Console.WriteLine("{0}={1}", kvp[0], kvp[1]); 
    } 
} 
Powiązane problemy