Regex查找调用另一个特定函数的函数名

hujrc8aj  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(131)

假设你有一个包含多个方法的C#类。其中一些方法调用了一个函数DoWorkAsync。我需要一个正则表达式来找到函数的名称,并且只找到调用DoWorkAsync的函数。幸运的是,所有函数都有相同的签名:public object %name%(List<object> params),并且都以return null;安全措施结束。
举例来说:

...
public object AddData(List<object> params) {
    DoSomething();
    DoSomething();
    DoSomething();
    return null;
}

public object ProcessData(List<object> params) {
    DoSomething();
    DoWorkAsync(); //this is an invocation
    DoSomething();
    return null;
}

public object RemoveData(List<object> params) {
    DoSomething();
    DoSomething();
    DoSomething();
    return null;
}
...

字符串
我首先尝试了这个简单的正则表达式(我使用SingleLine Option,所以“.”匹配新行):
@"public object (?'funcname'.+?)\(List<object> params\).+?DoWorkAsync\(\);"
这样做的问题是第一个函数总是匹配,funcname将是AddData而不是ProcessData,因为它最终会在打开函数后找到对DoWorkAsync的调用。它不知道函数结束了,新的函数开始了。好的,所以我读了关于平衡正则表达式的文章,它应该处理这种事情。然后我尝试了这样的东西:
@"(?:public object (?'funcname'.+?)\(List<object> params\)(?'count')|.+?DoWorkAsync\(\);.+?|return null;(?'-count'))*(?(count)(?!))"
这不起作用,因为它将匹配两个打开的函数和两个关闭的“return null”。我需要它在没有首先找到函数调用的情况下找到return时失败。我尝试了其他排列,例如:
@"public object (?'funcname'.+?)\(List<object> params\).+?(?:public object .+?\(List<object> params\)(?!)|DoWorkAsync\(\);.+?).+?return null;"
但是没有任何效果。我认为平衡正则表达式可能是答案,如果我只能过滤最深的一层。或者,如果平衡不起作用,那么某种前瞻,这样在找到打开函数之后,如果你前瞻并在函数调用之前找到另一个打开函数,它就会失败。
有人知道吗?谢谢。

uxhixvfz

uxhixvfz1#

我能够弄清楚它。我不得不做嵌套正则表达式。下面是我做的:
1.查找所有带有此正则表达式的函数:@"public object (?'name'\S+)\(List<object> params\){(?'function'(?:[^{}]*|{(?'depth')|}(?'-depth'))*(?(depth)(?!)))}"
1.用我想替换的目标函数初始化一个队列:var queue = new Queue<string>(); queue.Enqueue("DoWorkAsync");
1.使用while循环遍历队列:

while (queue.Count > 0) {
  var toConvert = queue.Dequeue();
  ...
}

字符串
1.在while内部,执行for循环,将调用该函数的任何函数添加到队列中:

foreach (var caller in functions.Where(m => m.Groups["function"].Value.Contains(toConvert)).Select(m => m.Groups["name"].Value)) {
  queue.Enqueue(caller);
}


1.在foreach循环之后,但仍然在while循环中,我只是替换了调用和定义:

newSource = Regex.Replace(oldSource, $@"{toConvert}\((?'params'(?:[^\(\)]*|\((?'depth')|\)(?'-depth'))*(?(depth)(?!)))\)", $"await {toConvert}(${{params}})");
newSource = Regex.Replace(newSource, $@"public object {toConvert}\(List<object> params\)", $"public async Task<object> {toConvert}(List<object> params)");


就是这样!
我发现的一个警告是,如果任何函数是递归的,它将把自己添加到队列中并无限循环。这可以通过一组已经转换的函数并在foreach中过滤掉来解决。

相关问题