Regex: повторные группы захвата

Я должен проанализировать некоторые таблицы из текстового файла ASCII. Вот частичный образец:

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 также выдает следующее предупреждение:

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

Я сверился с их справочным файлом, но понятия не имею, как это решить.

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

3 ответа

Решение

В C# (модифицировано из этого примера):

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.

К сожалению, вам нужно повторить (…) 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]

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

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

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

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

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

Другие вопросы по тегам