Regex 正则表达式递归:匹配两个字符的开头

Regex 正则表达式递归:匹配两个字符的开头,regex,recursion,Regex,Recursion,我试图使用递归正则表达式来匹配bash式变量扩展。基本上,我应该能够匹配如下字符串: ${FOO=BAR} ${FOO=${BAR=BAZ}} 但也可以像这样处理输入: ${FOO=\${BAR=BAZ}} ${FOO={${BAR=BAZ}} 在第一种情况下,它应该匹配到,但不包括最后的},第二种情况应该完全匹配。这是因为我试图将两个字符的开头${与一个字符的结尾}相匹配。打开和关闭都应该能够逃脱 我已经得到了PCRE正则表达式\$\{(?:[^{}]\;(?R))*\}。但这不能正确处理

我试图使用递归正则表达式来匹配bash式变量扩展。基本上,我应该能够匹配如下字符串:

${FOO=BAR}
${FOO=${BAR=BAZ}}
但也可以像这样处理输入:

${FOO=\${BAR=BAZ}}
${FOO={${BAR=BAZ}}
在第一种情况下,它应该匹配到,但不包括最后的
}
,第二种情况应该完全匹配。这是因为我试图将两个字符的开头
${
与一个字符的结尾
}
相匹配。打开和关闭都应该能够逃脱

我已经得到了PCRE正则表达式
\$\{(?:[^{}]\;(?R))*\}
。但这不能正确处理转义。如果我将其修改为
(?:^[^\])(?:\\\\\\)*(\$\{(?:[^{}][124;(?R))*})
,则只有最外层的转义正确匹配


这可以用regex来完成吗,还是我最好只编写一个pyparsing解析器?

您可以尝试以下模式:

(?s)\\.(*SKIP)(*F)|(?s)(\${(?>[^$}\\]+|\\.|(?1))*})

详情:

(?s)
\\.               # an escaped character
(*SKIP)           # skip the matched content if the pattern fails later
(*F)              # force the pattern to fail
|
(?s)
(
    \${
    (?>           # open a atomic group
        [^$}\\]+  # all that is not a backslash, a $ or a }
      |           # OR
        \\.       # an escaped character
      |           # OR
        (?1)      # recurse to group 1
    )*            # repeat the atomic group zero or more times
    }               
)
其主要思想是避免将逃逸的美元后跟开头的卷曲括号视为开头标记

注意:对于每个分支,不使用内联修饰符
(?s)
,您可以删除它们并使用全局修饰符
s

为了完全严格,您可以通过在原子组中添加可选的
\$(?!{)
(递归之前)来允许内容中的
$
后面没有一个开放的花括号

关于
(*跳过)
(*失败)

(*SKIP)
(*FAIL)
称为回溯控制动词

当模式失败时,NFA正则表达式引擎的默认行为是使用回溯机制。这些动词允许控制该机制

更具体地说,组合
子模式(*SKIP)(*FAIL)
的目标是从匹配结果中排除子模式匹配的内容,并禁止正则表达式引擎使用匹配的子字符串尝试其他任何操作。子字符串被跳过

你可以看到一个

关于原子分组:

An是一个非捕获组。唯一的区别是,一旦到达右括号,正则表达式引擎就不再允许回溯括号之间匹配的字符。它只能返回到组之前的位置。原子组使匹配的子字符串不可分割(原子)


在这里,原子群阻止了这种结构可能发生的情况(?:A+| B)+如果模式稍后失败。

您试图匹配的内容有点不清楚,尤其是最后两个示例。为什么最后一个示例没有平衡的花括号?第一个示例将跳过两个字符的开头序列:我正在尝试将
${
}
匹配,而不仅仅是
{
}
。在第一种情况下,整个匹配应该是
${FOO=\${BAR=BAZ}
,而在第二种情况下,整个字符串应该匹配,因为第二个
{
实际上没有打开任何内容。我编辑了这个问题以使这一点更清楚。因此,您需要允许一个小括号(前面没有
$
)作为内容。不是吹毛求疵,但是你能在任何地方看到双重
$
或独立
$
吗?哇,太好了!原子群到底是什么?我也不知道
(*SKIP)
(*FAIL)
。你能更详细地解释一下吗?