在.NET的RegEx中,我可以从Capture对象获取Groups集合吗?

bob*_*bob 8 .net regex capture

.NET在其RegularExpression实现中提供了一个Capture集合,因此您可以获取给定重复组的所有实例,而不仅仅是它的最后一个实例.这很好,但我有一个重复的小组与子组,我试图进入子组,因为他们在组下相关,并找不到方法.有什么建议?

我看过其他一些问题,例如:

但我发现没有适用的答案是肯定的("是的,这里是怎么样")或否定的("不,不能做".).

对于一个人为的例子说我有一个输入字符串:

abc d x 1 2 x 3 x 5 6 e fgh
Run Code Online (Sandbox Code Playgroud)

其中"abc"和"fgh"表示我想在较大文档中忽略的文本,"d"和"e"包裹感兴趣的区域,并且在该感兴趣的区域内,"xn [n]"可以重复任何次数.这是我感兴趣的"x"区域中的那些数字对.

所以我正在使用这个正则表达式模式解析它:

.*d (?<x>x ((?<fir>\d+) )?((?<sec>\d+) )?)*?e.*
Run Code Online (Sandbox Code Playgroud)

它将在文档中找到一个匹配项,但多次捕获"x"组.以下是我想在此示例中提取的三对:

  • 1,2
  • 3
  • 5,6

但我怎么能得到它们?我可以做以下(在C#中):

using System;
using System.Text;
using System.Text.RegularExpressions;

string input = "abc d x 1 2 x 3 x 5 6 e fgh";
string pattern = @".*d (?<x>x ((?<fir>\d+) )?((?<sec>\d+) )?)*?e.*";
foreach (var x in Regex.Match(input, pattern).Groups["x"].Captures) {
    MessageBox.Show(x.ToString());
}
Run Code Online (Sandbox Code Playgroud)

因为我引用组"x"我得到这些字符串:

  • x 1 2
  • x 3
  • x 5 6

但这并不能让我了解这些数字.所以我可以独立完成"fir"和"sec",而不仅仅是"x":

using System;
using System.Text;
using System.Text.RegularExpressions;

string input = "abc d x 1 2 x 3 x 5 6 e fgh";
string pattern = @".*d (?<x>x ((?<fir>\d+) )?((?<sec>\d+) )?)*?e.*";
Match m = Regex.Match(input, pattern);
foreach (var f in m.Groups["fir"].Captures) {
    MessageBox.Show(f.ToString());
}

foreach (var s in m.Groups["sec"].Captures) {
    MessageBox.Show(s.ToString());
}
Run Code Online (Sandbox Code Playgroud)

要得到:

  • 1
  • 3
  • 2
  • 6

但后来我无法知道它是第二对缺少"4",而不是其他一对.

那么该怎么办?我知道我可以轻松地在C#中解析这个问题,甚至可以在"x"组上进行第二次正则表达式测试,但是由于第一次RegEx运行已经完成了所有工作并且结果已知,所以似乎应该有一种方法操纵Match对象以获得我需要的东西.

请记住,这是一个人为的例子,现实世界的情况稍微复杂一些,所以只需要额外抛出C#代码就会很痛苦.但是如果现有的.NET对象无法做到,那么我只需要了解它,并且我会继续前进.

思考?

Dan*_*ner 5

我不知道完全构建的解决方案,并且在快速搜索后找不到一个,但这并不排除存在一个的可能性.

我最好的建议是使用IndexLength属性来查找匹配的捕获.看起来不是很优雅但是在编写一些扩展方法之后你可能会想出一些非常好的代码.

var input = "abc d x 1 2 x 3 x 5 6 e fgh";

var pattern = @".*d (?<x>x ((?<fir>\d+) )?((?<sec>\d+) )?)*?e.*";

var match = Regex.Match(input, pattern);

var xs = match.Groups["x"].Captures.Cast<Capture>();

var firs = match.Groups["fir"].Captures.Cast<Capture>();
var secs = match.Groups["sec"].Captures.Cast<Capture>();

Func<Capture, Capture, Boolean> test = (inner, outer) =>
    (inner.Index >= outer.Index) &&
    (inner.Index < outer.Index + outer.Length);

var result = xs.Select(x => new
                            {
                                Fir = firs.FirstOrDefault(f => test(f, x)),
                                Sec = secs.FirstOrDefault(s => test(s, x))
                            })
               .ToList();
Run Code Online (Sandbox Code Playgroud)

这是一种使用以下扩展方法的可能解决方案.

internal static class Extensions
{
    internal static IEnumerable<Capture> GetCapturesInside(this Match match,
         Capture capture, String groupName)
    {
        var start = capture.Index;
        var end = capture.Index + capture.Length;

        return match.Groups[groupName]
                    .Captures
                    .Cast<Capture>()
                    .Where(inner => (inner.Index >= start) &&
                                    (inner.Index < end));
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以按如下方式重写代码.

var input = "abc d x 1 2 x 3 x 5 6 e fgh";

var pattern = @".*d (?<x>x ((?<fir>\d+) )?((?<sec>\d+) )?)*?e.*";

var match = Regex.Match(input, pattern);

foreach (Capture x in match.Groups["x"].Captures)
{
    var fir = match.GetCapturesInside(x, "fir").SingleOrDefault();
    var sec = match.GetCapturesInside(x, "sec").SingleOrDefault();
}
Run Code Online (Sandbox Code Playgroud)