用于查找调用另一个特定函数的函数名称的正则表达式

Regex to find the name of functions that called another specific function

提问人:superjugy 提问时间:11/7/2023 更新时间:11/11/2023 访问量:58

问:

假设您有一个具有多个方法的 C# 类。其中一些方法调用函数。我需要一个正则表达式来查找函数的名称,并且仅查找调用 DoWorkAsync 的函数。幸运的是,所有功能都具有相同的签名:并且都以保护措施结束。DoWorkAsyncpublic 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\(\);"

这样做的问题是第一个函数将始终匹配,而不是因为它最终会在打开函数后找到对的调用。它不知道函数结束了,新函数开始了。好的,所以我读到了平衡正则表达式,它应该处理这种东西。然后我尝试了类似的东西:funcnameAddDataProcessDataDoWorkAsync

@"(?:public object (?'funcname'.+?)\(List<object> params\)(?'count')|.+?DoWorkAsync\(\);.+?|return null;(?'-count'))*(?(count)(?!))"

这不起作用,因为它将再次匹配两个打开函数和两个关闭“return null”。如果它在没有首先找到函数调用的情况下找到返回值,我需要它失败。我尝试了其他排列,例如:

@"public object (?'funcname'.+?)\(List<object> params\).+?(?:public object .+?\(List<object> params\)(?!)|DoWorkAsync\(\);.+?).+?return null;"

但没有任何效果。我认为,如果我能够只过滤最深的层次,那么平衡正则表达式可能是答案。或者,如果平衡不起作用,则进行某种展望,以便在找到打开函数后,如果您在函数调用之前向前查看并找到另一个打开函数,则它将失败。

有人知道吗?谢谢。

C# 正则表达式

评论

0赞 Auditive 11/8/2023
这难道不是XY问题的又一次发生吗?请问你想达到什么目的?也许你想要?Find usagesDoWorkAsync
0赞 Ralf 11/8/2023
要解析语言文件,我倾向于使用语言词法分析器/解析器而不是正则表达式,即使您认为该特定文件的结构相当容易。
0赞 superjugy 11/8/2023
@Auditive您可能已经注意到,我想要查找的函数调用是 Async。我需要 ot ifnd 用法来替换调用函数,使其也是异步的。我知道我可以使用我的 IDE 来查找用法,但此代码是生成的代码,我需要在管道中自动处理和转换。
0赞 superjugy 11/8/2023
@Ralf是否有可以从 C# 使用的库?这是我需要为管道自动化的东西
0赞 Auditive 11/8/2023
无法使用正则表达式有效地“分析”C# 代码。例如,您可以在带有源文件的整个文件夹上使用简单的Notepad ++,并手动检查每一行。如果你正在寻找一个“解析器”——在这样的时候问是不合适的,但也已经回答了Ctrl+F

答:

0赞 superjugy 11/11/2023 #1

我能够弄清楚。不过,我必须做嵌套正则表达式。这是我所做的:

  1. 使用此正则表达式查找所有函数:@"public object (?'name'\S+)\(List<object> params\){(?'function'(?:[^{}]*|{(?'depth')|}(?'-depth'))*(?(depth)(?!)))}"
  2. 使用我要替换的目标函数初始化队列:var queue = new Queue<string>(); queue.Enqueue("DoWorkAsync");
  3. 使用 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 中过滤掉它们来解决。