【问题标题】:Regex: Repeated capturing groups正则表达式:重复捕获组
【发布时间】:2011-03-11 12:01:27
【问题描述】:

我必须从 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 还会发出以下警告:

你重复了捕获组 本身。该组将仅捕获 最后一次迭代。放一个捕捉 围绕重复的组进行分组 捕获所有迭代。

我查阅了他们的帮助文件,但我不知道如何解决这个问题。

如何分别捕获每一列?

【问题讨论】:

标签: c# .net regex


【解决方案1】:

很遗憾,您需要重复 (…) 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]

【讨论】:

  • 肯尼,感谢您的及时回复!我现在实际上正在使用该模式。但我想知道使用重复捕获组是否有更好的解决方案。
  • @invarbrass:不是我知道的重复捕获组。如果您不尝试一次性使用正则表达式,它们通常效果最好。
  • KennyTM:谢谢!您的解决方案有效 - 我正在做类似的事情,尽管不那么优雅。
  • .NET 的特殊之处在于它保留了中间捕获!见蒂姆的回答和stackoverflow.com/questions/3029127/…
【解决方案2】:

在 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.

【讨论】:

  • 感谢您的提醒。我正在调查 Group.Captures 属性。
  • Captures 是一个简洁的功能,但在这里看起来有点矫枉过正。为什么不只在空白处分割每一行?即使您使用正则表达式来验证行的格式,它的工作量仍然较少。
【解决方案3】:

如果您想知道警告出现的原因,那是因为您的捕获组匹配多次(如您指定的 8 次),但捕获变量只能有一个值。它被分配最后一个匹配的值。

question 1313332 中所述,使用正则表达式通常无法检索这些多个匹配项,尽管 .NET 和 Perl 6 对此提供了一些支持。

警告表明您可以在整个集合周围放置另一个组,如下所示:

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

然后您将能够看到所有列,但它们当然不会被分开。因为通常不可能单独捕获它们,所以更常见的意图是捕获所有它们,并且警告有助于提醒您这一点。

【讨论】:

    猜你喜欢
    • 2017-09-13
    • 2017-01-07
    • 2019-12-19
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    相关资源
    最近更新 更多