.net 正则表达式解析具有任意深度的函数

.net 正则表达式解析具有任意深度的函数,.net,regex,recursive-regex,.net,Regex,Recursive Regex,我正在为其中包含的函数解析一种简单语言(Excel公式)。函数名必须以任意字母开头,后跟任意数量的字母/数字,并以空格结尾(中间无空格)。例如MyFunc(。该函数可以包含任何参数,包括其他函数,并且必须以关闭参数结尾)。当然,parens中的数学是允许的,=MyFunc((1+1))和(1+1)不应该被检测为函数,因为它不符合我刚才描述的函数规则。我的目标是识别公式中最高级别的函数调用,识别函数名,提取参数。通过这些参数,我可以递归地查找其他函数调用 使用这个,我破解了以下正则表达式。似乎没有

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

使用这个,我破解了以下正则表达式。似乎没有人能做到这一点。它们都未能通过下面粘贴的测试用例

这应该可以工作,但完全失败:

(?<name>[a-z][a-z0-9]*\()(?<body>(?>[a-z][a-z0-9]*\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\)
这应匹配为:

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正则表达式。

这在.net正则表达式的功能范围内。这是一个有效的演示:

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);
      }
    }
  }
}
任何未加名称的未结对价都不被计算在内,因为它与最后一个备选方案相匹配,
|.?
),这与结对价失去了平衡。这也意味着您无法匹配像
=MyFunc((1+1))
这样的公式,您在本文中提到了这些公式,但示例中没有包括这些公式。(我加了一套帕伦来演示。)

编辑:以下是支持非重要引用参数的版本:

  Regex r = new Regex(@"
    (?<name>[a-z][a-z0-9]*\()
      (?<body>
        (?>
           \((?<DEPTH>)
         |
           \)(?<-DEPTH>)
         |
           ""[^""]+""
         |
           [^()""]+
        )*
        (?(DEPTH)(?!))
      )
    \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
Regex r=新的Regex(@”
(?[a-z][a-z0-9]*\()
(?
(?>
\((?)
|
\)(?)
|
""[^""]+""
|
[^()""]+
)*
(?(深度)(?!)
)
\)“,RegexOptions.IgnoreCase | RegexOptions.ignorepattern空白);

这不能用正则表达式来完成,因为正则表达式只能用于。您需要使用解析器。还有,这是家庭作业吗?我认为这种方法没有什么意义。我宁愿为此编写解析器。@JoshD-为什么这不是一种常规语言?这违反了什么原则?我不知道。不,这不是作业(我能把-1倒过来吗?=)。我是一名专业的软件工程师,需要解决Excel中的错误。在发布之前,我花了很多时间试图让它工作。@steinar-我可以一直使用这种方法,但我的正则表达式非常接近工作状态。我只需要找个时间看看它,告诉我它的缺点在哪里。我不太确定它是否能正常工作。正如JoshD提到的,这不是解决这一系列问题的工具。您最好编写一个相当简单的解析器,即使是手工编写(与使用YACC或类似的东西相比),括号使之非常简单。啊!谢谢我自己也不会明白的。这是正确的解决方案。谢谢你相信我可以用Regex=)好的…我会把它作为额外的学分/绝对不需要/仍然感谢你解除了我的阻碍:有没有办法忽略引号中的paren?完成之前提交的最后一条评论。所以这个想法是=Func(“Hel(o”)解析很好。我把它扔出去是因为我感觉将来会有人问这个问题。出于我的目的,我可以保证字符串不会有不平衡的paren。@SFun28:您所需要做的就是添加另一个选项来匹配完整的、带引号的节,然后在f中排除引号和paren最后,其他所有选项:
(?>)((?)|\)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)((?)+(?(深度)(?))*(?)似乎打破了一些测试(老兄,我不能在这个文本框中按回车键换行)
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(...)...)
  Regex r = new Regex(@"
    (?<name>[a-z][a-z0-9]*\()
      (?<body>
        (?>
           \((?<DEPTH>)
         |
           \)(?<-DEPTH>)
         |
           ""[^""]+""
         |
           [^()""]+
        )*
        (?(DEPTH)(?!))
      )
    \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);