【问题标题】:Repeatable, complex regular expression, with dot '.' delimited separators可重复的复杂正则表达式,带有点“。”分隔符
【发布时间】:2013-07-19 21:32:41
【问题描述】:

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

除了,现在我需要重复一遍。

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

例如,如果这是我的正则表达式捕获的内容:<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 来分隔字符串段。但我认为如果对正则表达式的增强足够简单,我会跳过额外的拆分步骤,并重新使用正则表达式作为验证机制。

编辑:

作为齿轮中的额外扳手,我想禁止使用任何点 '.'字符串开头或结尾的字符。它们应该只作为路径段之间的分隔符存在。

【问题讨论】:

  • 一种简单的方法是在. 上拆分字符串,然后在每个字符串上运行您的正则表达式。
  • 我目前正在这样做。我想如果对正则表达式的增强足够简单,我就可以放弃 string.Split,还可以在解析字符串之前对其进行验证。
  • 换句话说,您正在寻找从字符串的开头到结尾用点分隔的连续匹配,不是吗?
  • 在大多数语言中(/“正则表达式的味道”),无法计算有多少捕获组与 * 或 + 匹配...但是,幸运的是,在 .NET 中这很容易。见:stackoverflow.com/questions/3029127/…
  • 四个优秀的答案;我需要测试和消化每一种方法,以确定哪种方法最适合我。如果我能给出四个“正确答案”复选标记...

标签: c# regex


【解决方案1】:

您实际上并不需要使用任何环视。您可以在主模式前放置一个(^|\.),然后在其后放置一个+。这将允许您制作一个重复的、. 分隔的序列。为简单起见,我还建议您将 &lt;index&gt; 组合并到一个捕获中(我使用 * 来匹配任意数量的索引,但您也可以轻松地使用 {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})*$

【讨论】:

  • 我喜欢这种简化。我可能需要将结尾的 * 更改为 {0,3},因为有 3 个索引访问器的硬性限制。但这无关紧要。您规定的 (^|\.) 模式能否确保没有头尾点? IE。点应该只存在于路径段之间 - 而不是在开头或结尾。
  • 插入正则表达式后,它似乎只生成一个匹配项。它“吃掉”所有前面的路径段,并将整个事物视为一个“成员”组。
  • @BTownTKD 你是对的,它确实允许领先.(我会努力解决这个问题),但它绝对不应该“吃掉”前面的部分。您可能只需要调整迭代结果的方式,因为现在每个组都可以有多个捕获。
  • 我正在使用 Derek Slager 的在线 .NET 正则表达式测试器。它通常遍历所有匹配和捕获,并以良好的格式显示它们。 derekslager.com/blog/posts/2007/09/…
  • @BTownTKD 我怀疑该工具不能处理多个捕获并且只显示每组的最后一个捕获(大多数在线正则表达式测试人员都是这样工作的)。我已经在 LINQPad 中对此进行了测试,它确实允许您分别处理每个捕获。请参阅我更新的答案以获取不允许前导点的替代方法。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 我怎样才能改变这个来禁止点'。'字符在开头还是结尾?我想确保它们只存在于路径段之间。
    • 我尝试将其插入我的测试程序(来自我的回答),它显示 "member" 作为整个匹配项
    【解决方案4】:

    试试这个野兽:

    (?<=^|\.)?((?<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);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-20
      • 2014-06-06
      • 1970-01-01
      相关资源
      最近更新 更多