7

我有一个正则表达式。它包含一个必需的命名捕获组和一些可选的命名捕获组。它捕获单个匹配项并将这些部分解析为我需要的命名组。

除了,现在我需要它重复。

本质上,我的正则表达式表示(可能)更长的字符串中的单个原子单元。目标字符串通常不会完全匹配我的正则表达式,而是包含重复的正则表达式实例,用点“。”分隔。特点。

例如,如果这是我的正则表达式捕获的内容:<some match>

实际的字符串可能如下所示:

  • <some match>
  • <some match>.<some other match>
  • <some match>.<some other match>.<yet another match>

修改原始正则表达式以解释重复模式同时忽略点的最简单方法是什么?

我不确定它是否真的需要,但这是我用来捕获单个段的正则表达式。同样,我想增强它以考虑可选的附加部分。我想让每个段在结果集中显示为另一个“匹配”;

^(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])?(?:\[(?<index2>[0-9]+)\])?(?:\[(?<index3>[0-9]+)\])?$

它旨在解析类路径,最多具有三个可选的索引访问器。(即“ member.sub_member[0].sub_sub_member[0][1][2]”)

我怀疑答案涉及向前看或向后看,对此我并不完全熟悉。

我目前使用 String.Split 来分隔字符串段。但我认为,如果对正则表达式的增强足够简单,我会跳过额外的拆分步骤,并重新使用正则表达式作为验证机制。

编辑:

作为齿轮中的附加扳手,我想禁止任何点“。” 字符串开头或结尾的字符。它们应该只作为路径段之间的分隔符存在。

4

4 回答 4

3

你真的不需要使用任何环顾四周。你可以(^|\.)在你的主要模式前面放一个,然后+在它后面放一个。这将允许您制作一个重复的、.分隔的序列。为简单起见,我还建议您将您的<index>组合并到一个捕获中(我曾经*匹配任意数量的索引,但您也可以轻松地使用{0,3}最多匹配 3 个)。最终的模式是:

(?:(?:^|\.)(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])*)+$

例如:

var input = "member.sub_member[0].sub_sub_member[0][1][2]";
var pattern = @"(?:(?:^|\.)(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])*)+$";
var match = Regex.Match(input, pattern);
var parts = 
    (from Group g in match.Groups
     from Capture c in g.Captures
     orderby c.Index
     select c.Value)
    .Skip(1);

foreach(var part in parts)
{
    Console.WriteLine(part);
}

这将输出:

member
sub_member
0
sub_sub_member
0
1
2

更新:此模式将确保字符串不能有任何前导或尾随点。这是一个怪物,但它应该工作:

^(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\]){0,3}(?:\.(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\]){0,3})*$

或者这个,虽然我不得不放弃我的“不环顾四周”的想法:

^(?!\.)(?:(?:^|\.)(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\]){0,3})*$
于 2013-07-19T12:20:34.107 回答
1

最简单的方法可能是使用string.Split'.'分割字符串。字符,然后将您的正则表达式应用于结果数组中的每个元素。无论如何,这么长的正则表达式都会有一些残酷的性能和潜在的前瞻性/落后问题。

于 2013-07-19T12:19:41.870 回答
1

试试这个野兽:

(?<=^|\.)?((?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])?(?:\[(?<index2>[0-9]+)\])?(?:\[(?<index3>[0-9]+)\])?)(?=\.){0,3}$?

这是一个示例控制台应用程序:

class Program
{
    public static void Main()
    {
        var input = @"member.sub_member[0].sub_sub_member[0][1][2]";
        var matches = Regex.Matches(input, @"(?<=^|\.)?((?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])?(?:\[(?<index2>[0-9]+)\])?(?:\[(?<index3>[0-9]+)\])?)(?=\.){0,3}$?");
        foreach (Match match in matches)
        {
            Console.Write("Member: {0} Index {1} Index2: {2} Index3 {3}\r\n", 
                match.Groups["member"].Value,
                match.Groups["index"].Value,
                match.Groups["index2"].Value,
                match.Groups["index3"].Value);
        }
    }
}
于 2013-07-19T12:22:01.190 回答
1

您可以使用\G确保有连续的结果和前瞻来检查模式后面是点还是字符串的结尾:

var pattern = @"(?:^|\G\.)(?<member>[A-Za-z_][A-Za-z0-9_]*)(?:\[(?<index>[0-9]+)\])?(?:\[(?<index2>[0-9]+)])?(?:\[(?<index3>[0-9]+)])?(?=\.|$)";

来自 msdn:\G “比赛必须从上一场比赛结束的位置开始”

于 2013-07-19T12:25:31.527 回答