使用正则表达式匹配具有两个以上参数的函数

Match functions with more than two arguments with Regex

提问人:cabralpinto 提问时间:9/11/2023 最后编辑:cabralpinto 更新时间:9/12/2023 访问量:118

问:

我想在 Python 中编写一个正则表达式,该表达式匹配具有两个以上参数的函数,以便以下表达式匹配:

function(1, 2, 3)
function(1, 2, 3, 4)
function(1, function(1, 2), 3)
function(function(function(1, 2), 2), 2, 3, 4)

但以下情况并非如此:

function(1, 2)
function(1, function(1, function(1, 2)))
function(1, function(function(1), 2))

我最好的尝试是以下表达式,它仅适用于没有嵌套函数的情况:

\w+\((?:.*,){2,}.*\)

我应该用什么表达方式来代替?

Python 正则表达式 PCRE 括号

评论

6赞 mozway 9/11/2023
看起来解析器会是一个更好的工具
2赞 AdrianHHH 9/11/2023
请澄清问题。你的意思是“两个或更多”还是“两个以上”?开头句子中的单词与不应匹配的例子不匹配。
2赞 Swifty 9/11/2023
为什么不应该和匹配?据我所知,每个函数都有 2 个参数。function(1, 2)function(1, function(1, function(1, 2)))
0赞 InSync 9/11/2023
你是说“两个整数”吗?
1赞 Good Night Nerd Pride 9/11/2023
我认为尝试/使用正则表达式执行此操作是个坏主意。您的实际用例或要求是什么?为什么需要解析函数调用?

答:

0赞 Timeless 9/11/2023 #1

为了好玩,您可以递归地用占位符(例如连字符)替换最右边的函数,并且只有在最后,计算每个表达式中的剩余参数:

import re

MIN_ARGS = 3

#https://peps.python.org/pep-0008/#function-and-variable-names
pyfunc = r"\b[a-zA-Z_][a-zA-Z0-9_]*"

pat = fr"({pyfunc}\(.*)(?:{pyfunc})?({pyfunc}\(.+?\))(.+)"

def fn(s):
    if len(re.findall(pyfunc, s)) > 1:
        new_s = re.sub(pat, r"\1-\3", s)
        return fn(new_s)
    else:
        return s

正则表达式:[demo - 第一次迭代]

输出:

# `text` is the multistring holding your expressions

for exp in text.splitlines():
    if (n:=fn(exp).count(",")) > MIN_ARGS-2:
        print(f"{exp:<50}", n+1, f"{'MATCH':>10}")
    else:
        print(f"{exp:<50}", n+1, f"{'NO-MATCH':>10}")

function(1, 2, 3)                                  3      MATCH
function(1, 2, 3, 4)                               4      MATCH
function(1, function(1, 2), 3)                     3      MATCH
function(function(function(1, 2), 2), 2, 3, 4)     4      MATCH
function(1, 2)                                     2   NO-MATCH
function(1, function(1, function(1, 2)))           2   NO-MATCH
function(1, function(function(1), 2))              2   NO-MATCH

评论

我有一个 pandas 数据帧,里面有一堆这样的表达式。我会 喜欢删除具有两个以上函数调用的行 参数

然后,您可以使用与上面相同的逻辑使用布尔索引

import pandas as pd

df = pd.DataFrame(text.splitlines(), columns=["col"])

# is there at most two arguments ?
mask = [fn(exp).count(",") <= MIN_ARGS-2 for exp in df["col"]]

out = df.loc[mask]

输出:

print(out)

                                        col
4                            function(1, 2)
5  function(1, function(1, function(1, 2)))
6     function(1, function(function(1), 2))
0赞 tshiono 9/12/2023 #2

使用带有正则表达式的递归的方法:Pypi

  • “一个参数”的定义(引用者):
    既不包含逗号也不包含括号的字符串, 或函数调用。
    (可能的问题:如果参数看起来像? =>目前不考虑)
    (?2)a * (b + c)

    • 表达者:([^(),]+|(?3))
    • 然后扩展为:([^(),]+|(\b\w+\s*\((?:([^(),]+|(?3))(?:\s*,\s*(?2))*)*\)))
  • “参数”的定义:
    以逗号分隔的 0 个或多个参数列表,
    即逗号可以在 0 个或多个空格之前/后面
    (?2)

    • 表达者:(?:(?2)(?:\s*,\s*(?2))*)*
  • “函数调用”的定义(引用者):
    函数名称 + 0 个或多个空格 + '(' + 参数 + ')'
    (?3)

    • 表达者:(\b\w+\s*\((?:([^(),]+|(?3))(?:\s*,\s*(?2))*)*\))

然后,所需的模式,具有 3 个或更多参数的函数,可以是 表达者:
(\b\w+\s*\(([^(),]+|(\b\w+\s*\((?:([^(),]+|(?3))(?:\s*,\s*(?2))*)*\)))(?:\s*,\s*(?2)){2,}\))

法典:

import regex

str = '''
function(1, 2, 3)
function(1, 2, 3, 4)
function(1, function(1, 2), 3)
function(function(function(1, 2), 2), 2, 3, 4)
function(1, 2)
function(1, function(1, function(1, 2)))
function(1, function(function(1), 2))
'''

pat = r'(\b\w+\s*\(([^(),]+|(\b\w+\s*\((?:([^(),]+|(?3))(?:\s*,\s*(?2))*)*\)))(?:\s*,\s*(?2)){2,}\))'

m = regex.findall(pat, str)
if m:
    print([x[0] for x in m])    # pick 1st element of tuples

输出:

['function(1, 2, 3)', 'function(1, 2, 3, 4)', 'function(1, function(1, 2), 3)', 'function(function(function(1, 2), 2), 2, 3, 4)']