Regex 正则表达式:紧跟在最后一个打开的括号之后的文本

Regex 正则表达式:紧跟在最后一个打开的括号之后的文本,regex,delphi,Regex,Delphi,我对正则表达式有一点了解,但目前它远远超出我的能力 我需要帮助在最后一个没有匹配的右括号的开括号之后立即查找文本/表达式 它用于开发中的开源软件(ObjectPascal)的调用提示 以下是一些例子: ------------------------------------ Text I need ------------------------------------ aaa(xxx xxx aaa(xxx,

我对正则表达式有一点了解,但目前它远远超出我的能力

我需要帮助在最后一个没有匹配的右括号的开括号之后立即查找文本/表达式

它用于开发中的开源软件(ObjectPascal)的调用提示

以下是一些例子:

------------------------------------
Text                  I need
------------------------------------
aaa(xxx               xxx
aaa(xxx,              xxx
aaa(xxx, yyy          xxx
aaa(y=bbb(xxx)        y=bbb(xxx)
aaa(y <- bbb(xxx)     y <- bbb(xxx)
aaa(bbb(ccc(xxx       xxx
aaa(bbb(x), ccc(xxx   xxx
aaa(bbb(x), ccc(x)    bbb(x)
aaa(bbb(x), ccc(x),   bbb(x)
aaa(?, bbb(??         ??
aaa(bbb(x), ccc(x))   ''
aaa(x)                ''
aaa(bbb(              ''
------------------------------------

For all text above the RegEx proposed by @Bohemian
(?<=\()(?=([^()]*\([^()]*\))*[^()]*$).*?(?=[ ,]|$)(?! <-)(?<! <-)
matches all cases.

For the below (I found these cases when implementing the RegEx in the software) not
------------------------------------
New text              I need
------------------------------------
aaa(bbb(x, y)         bbb(x, y)
aaa(bbb(x, y, z)      bbb(x, y, z)
------------------------------------
然而,我没能在比赛结束后立即做出适当的调整

任何人都可以帮忙吗?

这类似于。由于您使用的是PCRE,使用的是递归语法,所以实际上有一个解决方案

/
(?(DEFINE)                # define a named capture for later convenience
  (?P<parenthesized>      # define the group "parenthesized" which matches a
                          # substring which contains correctly nested
                          # parentheses (it does not have to be enclosed in
                          # parentheses though)
    [^()]*                # match arbitrarily many non-parenthesis characters
    (?:                   # start non capturing group
      [(]                 # match a literal opening (
      (?P>parenthesized)  # recursively call this "parenthesized" subpattern
                          # i.e. make sure that the contents of these literal ()
                          # are also correctly parenthesized
      [)]                 # match a literal closing )
      [^()]*              # match more non-parenthesis characters
    )*                    # repeat
  )                       # end of "parenthesized" pattern
)                         # end of DEFINE sequence

# Now the actual pattern begins

(?<=[(])                  # ensure that there is a literal ( left of the start
                          # of the match
(?P>parenthesized)?       # match correctly parenthesized substring
$                         # ensure that we've reached the end of the input
/x                        # activate free-spacing mode
其中
normal
[^()]
special
[(](?p>括号内)[)]
。这种技术被称为。它用于匹配任何具有该结构的对象

nnnsnnsnnnnsnnsnn
其中,
n
normal
匹配,
s
special
匹配

在这个特殊的例子中,事情要复杂一些,因为我们也在使用递归
(?P>括号)
递归地使用括号中的
模式(它是该模式的一部分)。您可以查看
(?P>…)
语法,有点像反向引用-除了引擎不尝试匹配组
匹配的内容,而是再次应用它的子模式

还要注意,对于正确插入括号的模式,我的模式不会给您一个空字符串,但会失败。你可以通过省去后顾之忧来解决这个问题。实际上不需要查找,因为引擎总是返回最左边的匹配项

编辑:根据两个示例判断,您实际上并不希望所有内容都位于最后一个不匹配的括号之后,而只希望所有内容位于第一个逗号之前。您可以使用我的结果并在
上拆分,或者尝试Bohemian的答案

进一步阅读:

  • (包括命名组)
  • 杰弗里·弗里德尔(Jeffrey Friedl)在他的书中介绍了“展开循环”,但我认为我上面链接的帖子给出了一个很好的概述
  • 使用
    (?(定义)…)
    实际上是滥用了另一个名为的特性。解释它是如何工作的-只需在页面中搜索“定义仅供引用的子模式”
编辑:我注意到您在问题中提到您正在使用Object Pascal。在这种情况下,您可能没有实际使用PCRE,这意味着不支持递归。在这种情况下,这个问题不可能有完整的正则表达式解决方案。如果我们施加一个限制,比如“在最后一个不匹配的括号之后只能有一个嵌套级别”(如所有示例中所示),那么我们可以想出一个解决方案。同样,我将使用“展开循环”来匹配表单的子字符串
xxx(xxx)xxx(xxx)xxx

(?<=[(])         # make sure we start after an opening (
(?=              # lookahead checks that the parenthesis is not matched
  [^()]*([(][^()]*[)][^()]*)*
                 # this matches an arbitrarily long chain of parenthesized
                 # substring, but allows only one nesting level
  $              # make sure we can reach the end of the string like this
)                # end of lookahead
[^(),]*([(][^()]*[)][^(),]*)*
                 # now actually match the desired part. this is the same
                 # as the lookahead, except we do not allow for commas
                 # outside of parentheses now, so that you only get the
                 # first comma-separated part
然后,要获取第一个未赋值逗号之前的所有内容,可以再次遍历该结果:

nestingLevel = 0
while you can read another character from the string
    if that character is "," and nestingLevel == 0, stop
    if that character is "(" increment nestingLevel
    if that character is ")" decrement nestingLevel
take a substring from the beginning of the string to the position at which
  you left the loop
这两个短循环将来会更容易被其他人理解,并且比正则表达式解决方案(至少有一个没有递归)灵活得多。

使用look aheads:

(?<=\()(?=([^()]*\([^()]*\))*[^()]*$).*?(\(.*?\))?(?=[ ,]|$)(?! <-)(?<! <-)

(?请参阅问题中公布的通过所有测试用例。

这与在哪里找到结果不太一致。对于输入
aaa(y(@m.buettner上一个版本只在一个测试用例上失败。我添加了一个前瞻来修复它。请参阅编辑的答案和rubularlink@Bohemian,非常感谢您的关注!您的新RegEx版本确实适用于所有原始测试用例。但是,当在开发软件中实现您的RegEx并对其进行测试时,我只发现了两个用例(参见编辑后的原始问题)它不匹配。请,您能做一个小的调整以匹配这些新案例吗?@jcfaria我做到了!这个正则表达式现在通过了您的所有测试案例。我添加了另一个可选术语,可以捕获目标末尾方括号中的任何文本。顺便说一句,这是我编写过的最长正则表达式:)@波希米亚人,很多人都感谢你的这个正则表达式!它真的通过了所有的测试用例。我正在读的一本书说长正则表达式完全符合我们的要求…似乎这就是一个例子!;)谢谢你的回答。但是,我对正则表达式的了解有点复杂。我使用免费工具(Expresso和EditPadPro)构建和测试正则表达式。两者都不理解带有注释和多行的正则表达式。请,您可以在一行中编写?@jcfaria您只需要删除注释和元素之间的所有空格:
(?(定义)(?P[^()]*(?:[(](?P>括号内)[)[^()]*)(?括号内)我已经完成了$
,但是这两个工具在这个正则表达式中都显示了错误:
(?P>括号)?
非法的组语法,零或一repetitions@jcfaria那么您的工具没有使用PCRE,也不支持递归。事实上,Expresso使用的是.NET风格(它支持EditPad Pro可能使用与RegexBuddy相同的引擎,它也不支持PCRE的递归。如果您最终将my expression与PCRE一起使用,它将正常工作。否则它无法正常工作,因为递归是PCRE独有的。此外,如果您既不使用.NET也不使用PCRE,由于嵌套结构,您无法完全解决问题@jcfaria我刚刚注意到您在问题中提到您正在使用Object Pascal。我想您正在使用?这不是PCRE,但只实现Perl正则表达式风格的一个子集。例如,它不支持递归(尽管它也没有提到lookarounds)。我将使用有限的替代方法编辑我的答案。+1用于优秀的测试用例
while you can read another character from the string
    if that character is "(", push the current position onto the stack
    if that character is ")", pop a position from the stack
# you've reached the end of the string now
if the stack is empty, there is no match
else the top of the stack is the position of the last unmatched parenthesis;
     take a substring from there to the end of the string
nestingLevel = 0
while you can read another character from the string
    if that character is "," and nestingLevel == 0, stop
    if that character is "(" increment nestingLevel
    if that character is ")" decrement nestingLevel
take a substring from the beginning of the string to the position at which
  you left the loop
(?<=\()(?=([^()]*\([^()]*\))*[^()]*$).*?(\(.*?\))?(?=[ ,]|$)(?! <-)(?<! <-)