在C#中使用非捕获组的正则表达式

我使用以下正则表达式

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 

这个想法是提取两个组,每个组包含一行(从联合编号,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是最好的方法吗? (我真的觉得我现在有两个问题。)

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 

我不会使用正则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().Select(c => c.Value)); 

在此处输入图像描述

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

 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+ (?\d+) \s+ (?[^\s]+) \s+ (?[^\s]+) \s+ (?[^\s]+)"; var result = Regex.Matches(data, pattern, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture) .OfType() .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 */ 

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

改为使用它:

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

这有一个非贪婪的前导部分,确保线匹配部分在新行(不在标题的中间)开始,并使用[\r\n\s]*以防新行不完全如你期待。

就个人而言,我会使用正则表达式,但我喜欢正则表达式:-)如果你碰巧知道字符串的结构总是[title] [换行符] [换行符] [行]那么也许它更直接(如果更少)灵活的)只是拆分换行并相应处理。

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