【问题标题】:Recursive regex for matching everything in parenthesis (PCRE)用于匹配括号中所有内容的递归正则表达式 (PCRE)
【发布时间】:2019-01-11 09:02:39
【问题描述】:

令我惊讶的是,我很难在 SO 上找到类似问题的答案。我想匹配某些功能中的所有内容。这个想法是删除无用的功能。

foo(some (content)) --> some (content)

所以我试图匹配函数调用中可以包含括号的所有内容。这是我的 PCRE 正则表达式:

(?<name>\w+)\s*\(\K
(?<e>
     [^()]+
     |
     [^()]*
         \((?&e)\)
     [^()]*
)*
(?=\))

https://regex101.com/r/gfMAIM/1

不幸的是,它不起作用,我真的不明白为什么。

【问题讨论】:

  • 被检查的函数调用是否总是在行的开头?
  • @Predicate 假设一切都是一行,但你可以在这一行找到多个函数调用:foo(); bar()\n
  • 有没有办法使括号在所需的匹配中不平衡?

标签: regex pcre


【解决方案1】:

您的组 e 模式没有做正确的工作,目前,它匹配具有 1 个深度级别的括号,因为您只递归了一次 e 模式。它需要匹配尽可能多的(...) 子字符串,因此,子程序模式需要在*+ 量化组内,甚至可以“简化”为(?&lt;e&gt;[^()]*(?:\((?&amp;e)\)[^()]*)*)

请注意,您的组 e 模式等于 (?&lt;e&gt;[^()]+|\((?&amp;e)\))*[^()]*\((?&amp;e)\) 附近是多余的,因为[^()]+ 替代方案将在当前深度级别上消耗() 以外的字符。

此外,您量化了 Group e 模式,使其成为 repeated capturing group,仅在上次迭代期间保持文本匹配。

你可以使用

(?<name>\w+)\s*\(\K(?<e>[^()]*(?:\((?&e)\)[^()]*)*)(?=\))

regex demo

详情

  • (?&lt;name&gt;\w+)\s*\(\K - 匹配中省略的 1+ 个单词字符、0+ 个空格和 (
  • (?&lt;e&gt; - 组开始e
    • [^()]* - 除了 () 之外还有 0+ 个字符
    • (?: - 非捕获组的开始:
      • \( - 一个 ( 字符
      • (?&amp;e) - 组 e 模式递归
      • \) - 一个)
      • [^()]* - 除了 () 之外还有 0+ 个字符
    • )* - 0 次或多次重复
  • ) - e 组结束
  • (?=\)) - ) 必须紧邻当前位置。

【讨论】:

  • 似乎OP建议的正则表达式不起作用的原因是*紧跟在(?&lt;e&gt;组之后,在固定regex01中添加未捕获组
  • 另一种更有效地避免回溯的解决方案是使用所有格量词,例如(?&lt;name&gt;\w+)\s*\(\K(?&lt;e&gt;(?:[^()]++|\((?&amp;e)\))*)\)
  • @NahuelFouilleul 是的,这更好一点。也可以使用原子组(?&lt;name&gt;\w+)\s*\(\K(?&lt;e&gt;(?&gt;[^()]+|\((?&amp;e)\))*)(?=\))。另外,请注意最后一个) 必须在前瞻中(嗯,至少这是 OP 逻辑)。
  • 你们solution 因括号不平衡而失败。可以出现在字符串中。
  • @Predicate 那些情况超出了范围,它们不能与递归匹配。只有在预期匹配的两端都知道特定上下文时才能处理它们。对于这些场景,您不能使用 通用 正则表达式。
【解决方案2】:

以下正则表达式在不采取额外步骤的情况下进行匹配:

(?<name>\w+)\s*(\((?<e>([^()]*+|(?2))+)\))

live demo here

但这不匹配以下在带引号的字符串中包含不平衡括号的字符串:

  • foo(bar = ')')
  • foo(bar(john = "(Doe..."))

所以你应该寻找的是:

(?<name>\w+)\s*(\((?<e>([^()'"]*+|"(?>[^"\\]*+|\\.)*"|'(?>[^'\\]*+|\\.)*'|(?2))+)\))

live demo here

正则表达式分解:

  • (?&lt;name&gt;\w+)\s* 匹配函数名和尾随空格
  • ( 集群启动
    • \( 匹配文字 (
    • (?&lt;e&gt; 命名捕获组的开始e
      • ( 捕获组 #2 的开始
        • [^()'"]*+ 匹配除()'" 之外的任何东西
        • |或者
        • "(?&gt;[^"\\]*+|\\.)*" 匹配双引号之间的任何内容
        • |或者
        • '(?&gt;[^'\\]*+|\\.)*' 匹配单引号之间的任何内容
        • |或者
        • (?2)递归第二个捕获组
      • )+ 尽可能重复,至少一次
    • )抓包结束
    • \) 匹配 ) 字面意思
  • )捕获组结束

【讨论】:

    【解决方案3】:

    我有没有recursion 的简单正则表达式。

    (?<=[\w ]{2}\().*(?=\))
    

    现在它处理不平衡的括号,但它不处理在一行上的多个函数。如果您知道函数之间的分隔符,则可以处理它。例如; 如果那是 Java 代码。

    Variant 2(连续更新多个函数):

    (?<=[\w ]\()[^;\n]*(?=\))
    

    Variant 3(允许在字符串中使用;):

    (?<=[\w ]\()([^;\n]|".*?")*(?=\))    
    

    Variant 4(转义字符串):

    (?<=[\w \n]\()(?:[^;\n"]|(?:"(?:[^"]|\\")*?(?<!\\)"))*(?=\))
    

    【讨论】:

    • 检查变体 2
    • 您可以改进第一个lookbehind,使其不仅检查一个字符,而且由于PCRE不允许具有不同长度的Lookbehinds,因此工作变得更加困难。可能需要更多的上下文。但我认为在( 之前只检查一个字符没有问题
    • 而且因为* 是贪婪的,所以它会一直到最后一个) 并且也不匹配;。不会出现平衡问题,允许一行上的多个函数(如果分隔符是;
    • 保持简短^^
    • @NahuelFouilleul 参见变体 3
    猜你喜欢
    • 2012-10-31
    • 1970-01-01
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    • 2018-12-02
    • 2016-08-22
    • 2020-01-20
    • 2016-05-10
    相关资源
    最近更新 更多