正则表达式以任意深度解析函数

时间:2010-10-27 00:12:56

标签: .net regex recursive-regex

我正在为其中包含的函数解析一个简单的语言(Excel公式)。函数名称必须以任何字母开头,后跟任意数量的字母/数字,并以打开的paren结尾(中间没有空格)。例如MyFunc(。该函数可以包含任何参数,包括其他函数,并且必须以close paren )结尾。当然,parens中的数学被允许=MyFunc((1+1))并且(1+1)不应该被检测为函数,因为它失败了我刚刚描述的函数规则。我的目标是识别公式中的最高级函数调用,识别函数名称,提取参数。通过参数,我可以递归地查找其他函数调用。

使用此tutorial我修改了以下正则表达式。似乎没有人能做到这一点。他们都在下面粘贴的测试用例中失败了。

工作但完全失败:

(?<name>[a-z][a-z0-9]*\()(?<body>(?>[a-z][a-z0-9]*\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\)

这适用于许多测试用例,但在下面的测试用例中失败了。我不认为它正确处理嵌套函数 - 它只是在嵌套中寻找开放的paren / close paren:

(?<name>[a-z][a-z0-9]*\()(?<body>(?>\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\)

这是打破他们所有人的测试:

=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year(A$5),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1

这应匹配为:

Date(ARGUMENTS1)
Weekday(ARGUMENTS2)
Where ARGUMENTS2 = Date(Year(A$5),Month(A$5),1)

相反它匹配:

ARGUMENTS2 = Date(Year(A$5),Month(A$5),1)-1)

我正在使用.net RegEx,它提供外部存储器。

2 个答案:

答案 0 :(得分:4)

这完全在.NET regex的功能范围内。这是一个有效的演示:

using System;
using System.Text.RegularExpressions;

namespace Test
{
  class Test
  {
    public static void Main()
    {
      Regex r = new Regex(@"
        (?<name>[a-z][a-z0-9]*\()
          (?<body>
            (?>
               \((?<DEPTH>)
             |
               \)(?<-DEPTH>)
             |
               [^()]+
            )*
            (?(DEPTH)(?!))
          )
        \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

      string formula = @"=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year((A$5+1)),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1";

      foreach (Match m in r.Matches(formula))
      {
        Console.WriteLine("{0}\n", m.Value);
      }
    }
  }
}

输出:

Date(Year(A$5),Month(A$5),1)

Weekday(Date(Year((A$5+1)),Month(A$5),1))

你的正则表达式的主要问题是你将函数名称作为递归匹配的一部分包括在内 - 例如:

Name1(...Name2(...)...)

任何没有名字的开放式球员都没有被计算在内,因为它与最后的选择|.?)相匹配,并且与最后的替补相提并论。这也意味着您无法匹配=MyFunc((1+1))之类的公式,您在文本中提到但未包含在示例中。 (我扔了一套额外的parens来证明。)

编辑:这是支持非重要引用的parens的版本:

  Regex r = new Regex(@"
    (?<name>[a-z][a-z0-9]*\()
      (?<body>
        (?>
           \((?<DEPTH>)
         |
           \)(?<-DEPTH>)
         |
           ""[^""]+""
         |
           [^()""]+
        )*
        (?(DEPTH)(?!))
      )
    \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

答案 1 :(得分:0)