15

我正在使用以下正则表达式

JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)*

关于以下类型的数据:

 JOINTS               DISPL.-X               DISPL.-Y               ROTATION


     1            0.000000E+00           0.975415E+01           0.616921E+01
     2            0.000000E+00           0.000000E+00           0.000000E+00

思路是提取两组,每组包含一行(以Joint Number开头,1、2等)C#代码如下:

string jointPattern = @"JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)*";
MatchCollection mc = Regex.Matches(outFileSection, jointPattern );
foreach (Capture c in mc[0].Captures)
{
    JointOutput j = new JointOutput();
    string[] vals = c.Value.Split();
    j.Joint = int.Parse(vals[0]) - 1;
    j.XDisplacement = float.Parse(vals[1]);
    j.YDisplacement = float.Parse(vals[2]);
    j.Rotation = float.Parse(vals[3]);
    joints.Add(j);
}

但是,这不起作用:它不是返回两个捕获的组(内部组),而是返回一个组:整个块,包括列标题。为什么会这样?C# 是否以不同的方式处理未捕获的组?

最后,RegExes 是最好的方法吗?(我真的觉得我现在有两个问题。)

4

4 回答 4

12

mc[0].Captures相当于mc[0].Groups[0].CapturesGroups[0]总是指整个比赛,所以只会有一个与之关联的 Capture。您要查找的部分在第 1 组中捕获,因此您应该使用mc[0].Groups[1].Captures.

但是您的正则表达式旨在一次匹配整个输入,因此该Matches()方法将始终返回一个 MatchCollection,其中只有一个 Match(假设匹配成功)。您不妨Match()改用:

  Match m = Regex.Match(source, jointPattern);
  if (m.Success)
  {
    foreach (Capture c in m.Groups[1].Captures)
    {
      Console.WriteLine(c.Value);
    }
  }

输出:

1            0.000000E+00           0.975415E+01           0.616921E+01
2            0.000000E+00           0.000000E+00           0.000000E+00
于 2013-03-02T06:12:03.363 回答
3

我只是不会Regex用于繁重的工作并解析文本。

var data = @"     JOINTS               DISPL.-X               DISPL.-Y               ROTATION


         1            0.000000E+00           0.975415E+01           0.616921E+01
         2            0.000000E+00           0.000000E+00           0.000000E+00";

var lines = data.Split('\r', '\n').Where(s => !string.IsNullOrWhiteSpace(s));
var regex = new Regex(@"(\S+)");

var dataItems = lines.Select(s => regex.Matches(s)).Select(m => m.Cast<Match>().Select(c => c.Value));

在此处输入图像描述

于 2013-03-02T03:38:34.827 回答
1

有两个问题:重复部分(?:...)没有正确匹配;并且.*是贪婪的并消耗所有输入,因此即使可以,重复部分也永远不会匹配。

改用这个:

JOINTS.*?[\r\n]+(?:\s*(\d+\s*\S*\s*\S*\s*\S*)[\r\n\s]*)*

这有一个非贪婪的前导部分,确保行匹配部分从新行开始(而不是在标题中间),并[\r\n\s]*在换行符不完全符合您期望的情况下使用。

就个人而言,我会为此使用正则表达式,但我喜欢正则表达式 :-) 如果您碰巧知道字符串的结构将始终是 [title][newline][newline][lines] 那么它可能更简单(如果更少灵活)只拆分换行符并相应地处理。

最后,您可以使用regex101.com或许多其他正则表达式测试站点之一来帮助调试您的正则表达式。

于 2013-03-02T03:22:19.787 回答
1

为什么不只捕获这些值而忽略其余部分。这是一个获取值的正则表达式。

string data = @"JOINTS DISPL.-X DISPL.-Y ROTATION
 1 0.000000E+00 0.975415E+01 0.616921E+01
 2 0.000000E+00 0.000000E+00 0.000000E+00";

string pattern = @"^
\s+
 (?<Joint>\d+)
\s+
 (?<ValX>[^\s]+)
\s+
 (?<ValY>[^\s]+)
\s+
 (?<Rotation>[^\s]+)";

var result = Regex.Matches(data, pattern, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)
                  .OfType<Match>()
                  .Select (mt => new
                  {
                    Joint = mt.Groups["Joint"].Value,
                    ValX  = mt.Groups["ValX"].Value,
                    ValY  = mt.Groups["ValY"].Value,
                    Rotation = mt.Groups["Rotation"].Value,
                  });
/* result is
IEnumerable<> (2 items)
Joint ValX ValY Rotation
1 0.000000E+00 0.975415E+01 0.616921E+01
2 0.000000E+00 0.000000E+00 0.000000E+00
*/
于 2013-03-02T03:38:53.667 回答