2010-07-03 3 views
9

Мне нужно разобрать некоторые таблицы из текстового файла ASCII. Вот частичный образец:Regex: Повторяющиеся группы захвата

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 

Таблица состоит из 1 столбца текста и 8 столбцов чисел с плавающей запятой. Я хотел бы захватить каждый столбец с помощью регулярного выражения.

Я довольно новичок в регулярных выражениях. Вот неисправный шаблон регулярного выражения, который я придумал:

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

Но шаблон фиксирует только первый и последний столбцы. RegexBuddy также выдает следующее предупреждение:

Вы повторили группу захвата . Группа будет захватывать только последнюю итерацию. Поместите захват группы вокруг повторяющейся группы до . Захватите все итерации.

Я проконсультировался с их файлом справки, но у меня нет подсказки относительно того, как это решить.

Как я могу записать каждый столбец отдельно?

+0

На каком языке вы используете? В .NET это легко. –

+0

@Tim: Да, я намереваюсь написать программу на C#. Но на данный момент я прототипирую его в Python. – invarbrass

+0

См. Также: http: // stackoverflow.com/questions/3029127/is-there-a-regex-flavor-that-allow-me-to-count-the-number-of-repetitions-matching/ – polygenelubricants

ответ

12

В C# (модифицированный из 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++; 
     } 
    } 
} 

Выход:

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

Спасибо за Берегись. Я просматриваю свойство Group.Captures. – invarbrass

+2

'Captures' - это аккуратная функция, но, похоже, здесь слишком много лишнего. Почему бы просто не разделить каждую строку на пробелы? Даже если вы используете регулярное выражение для проверки формата строки, это еще меньше работает. –

5

К сожалению, вам нужно повторить (…) 8 раз, чтобы получить каждый столбец отдельно.

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

Если код возможно, вы можете первый матч этих числовых столбцов в целом

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

затем разделить столбцы пробелами

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

Kenny, спасибо за быстрый ответ! Я фактически использую этот шаблон прямо сейчас. Но мне было интересно, есть ли лучшее решение, использующее повторяющиеся группы захвата. – invarbrass

+0

@ invarbrass: Не с повторяющимися группами захвата, о которых я знаю. Регулярные команды часто работают лучше всего, если вы не пытаетесь переусердствовать с ними одним выстрелом. –

+0

KennyTM: Спасибо! Ваше решение работает - я делал что-то подобное, хотя и намного менее элегантно. – invarbrass

4

Если вы хотите знать, что предупреждение появляется для, это потому, что ваша группа захвата соответствует несколько раз (8, как было указано), но переменная захвата может иметь только одно значение. Ему присваивается последнее значение.

Как описано в question 1313332, получение этих множественных совпадений обычно невозможно с помощью регулярного выражения, хотя .NET и Perl 6 имеют определенную поддержку для него.

предупреждение свидетельствует о том, что вы могли бы поставить еще одну группу по всему множеству, например:

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

Вы бы тогда быть в состоянии увидеть все столбцы, но, конечно, они не будут разделены. Поскольку их вообще невозможно фиксировать отдельно, более распространенное намерение состоит в том, чтобы зафиксировать все это, и предупреждение поможет вам в этом.