2010-07-03 20 views
9

Muszę parsować niektóre tabele z pliku tekstowego ASCII. Oto częściowa próbka:Regex: Powtarzające się grupy przechwytywania

QSMDRYCELL 11.00 11.10 11.00 11.00 -.90  11  11000  1.212 
RECKITTBEN 192.50 209.00 192.50 201.80 5.21  34  2850  5.707 
RUPALIINS 150.00 159.00 150.00 156.25 6.29  4  80  .125 
SALAMCRST 164.00 164.75 163.00 163.25 -.45  80  8250 13.505 
SINGERBD 779.75 779.75 770.00 773.00 -.89  8  95  .735 
SONARBAINS 68.00 69.00 67.50 68.00  .74  11  3050  2.077 

Tabela składa się z 1 kolumny tekstu i 8 kolumn liczb zmiennoprzecinkowych. Chciałbym przechwycić każdą kolumnę za pomocą wyrażeń regularnych.

Jestem całkiem nowy w wyrażeniach regularnych. Oto wadliwy schemat regex, który wymyśliłem:

(\S+)\s+(\s+[\d\.\-]+){8} 

Ale wzór przechwytuje tylko pierwszą i ostatnią kolumnę. RegexBuddy generuje również następujące ostrzeżenie:

Użytkownik sam powtórzył grupę przechwytującą . Grupa przechwyci ostatnią iterację tylko w . Umieść przechwytywaną grupę wokół powtarzającej się grupy, aby przechwycić wszystkie iteracje.

Sprawdziłem ich plik pomocy, ale nie mam pojęcia, jak rozwiązać ten problem.

Jak przechwycić każdą kolumnę osobno?

+0

Jakiego języka używasz? W .NET jest to łatwe. –

+0

@Tim: Tak, zamierzam napisać program w języku C#. Ale w tej chwili prototypuję go w Pythonie. – invarbrass

+0

Zobacz także: http: // stackoverflow.com/questions/3029127/is-there-a-regex-flavor-that-allow-me-to-count-the-number-of-repetitions-pasujący/ – polygenelubricants

Odpowiedz

12

w C# (zmodyfikowany z this example):

string input = "QSMDRYCELL 11.00 11.10 11.00 11.00 -.90  11  11000  1.212"; 
string pattern = @"^(\S+)\s+(\s+[\d.-]+){8}$"; 
Match match = Regex.Match(input, pattern, RegexOptions.MultiLine); 
if (match.Success) { 
    Console.WriteLine("Matched text: {0}", match.Value); 
    for (int ctr = 1; ctr < match.Groups.Count; ctr++) { 
     Console.WriteLine(" Group {0}: {1}", ctr, match.Groups[ctr].Value); 
     int captureCtr = 0; 
     foreach (Capture capture in match.Groups[ctr].Captures) { 
     Console.WriteLine("  Capture {0}: {1}", 
          captureCtr, capture.Value); 
     captureCtr++; 
     } 
    } 
} 

wyjściowa:

Matched text: QSMDRYCELL 11.00 11.10 11.00 11.00 -.90  11  11000  1.212 
... 
    Group 2:  1.212 
     Capture 0: 11.00 
     Capture 1: 11.10 
     Capture 2: 11.00 
...etc. 
+0

Dzięki za Heads-up. Zajmuję się właściwością Group.Captures. – invarbrass

+2

'Captures' to schludna funkcja, ale wygląda na to, że jest tu przesada. Dlaczego nie podzielić linii na białe spacje? Nawet jeśli użyjesz wyrażenia regularnego do sprawdzenia poprawności formatu linii, to jeszcze mniej pracy. –

5

Niestety, aby uzyskać każdą kolumnę osobno, należy powtórzyć 8-krotne powtórzenie (…).

^(\S+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)\s+([-.\d]+)$ 

Jeśli kod jest możliwe, można najpierw dopasować te kolumny numeryczne jako całość

>>> rx1 = re.compile(r'^(\S+)\s+((?:[-.\d]+\s+){7}[-.\d]+)$', re.M) 
>>> allres = rx1.findall(theAsciiText) 

następnie podzielić kolumny spacjami

>>> [[p] + q.split() for p, q in allres] 
+1

Kenny, dzięki za szybką odpowiedź! Obecnie używam tego wzoru. Ale zastanawiałem się, czy istnieje lepsze rozwiązanie, używając powtarzających się grup przechwytywania. – invarbrass

+0

@invarbrass: Nie w przypadku powtarzających się grup przechwytujących, o których jestem świadomy. Regeksy często działają najlepiej, jeśli nie próbujesz ich przesadzić jednorazowym strzałem. –

+0

KennyTM: Dzięki! Twoje rozwiązanie działa - robiłem coś podobnego, aczkolwiek dużo mniej elegancko. – invarbrass

4

Jeśli chcesz wiedzieć, co pojawia się ostrzeżenie o, to dlatego, że grupa przechwytywania mecze wielokrotnie (8, jak to określono) ale zmienna przechwytywania może mieć tylko jedną wartość. Zostaje przypisana ostatnia dopasowana wartość.

Jak opisano w question 1313332, pobieranie tych wielokrotnych dopasowań nie jest generalnie możliwe z wyrażeniem regularnym, chociaż .NET i Perl 6 mają pewne wsparcie dla niego.

Ostrzeżenie sugeruje, że można umieścić inną grupę wokół całego zestawu, tak:

(\S+)\s+((\s+[\d\.\-]+){8}) 

Można by następnie móc zobaczyć wszystkie kolumny, ale oczywiście nie byłyby rozdzielone. Ponieważ generalnie nie jest możliwe uchwycenie ich osobno, bardziej powszechną intencją jest uchwycenie ich wszystkich, a ostrzeżenie przypomina o tym.

Powiązane problemy