2014-10-26 165 views
1

我试图使用递归正则表达式来匹配类似bash的变量扩展。基本上,我应该能够匹配类似下面的字符串:正则表达式递归:匹配两个字符的开头

${FOO=BAR} 
${FOO=${BAR=BAZ}} 

而且处理的投入是这样的:

${FOO=\${BAR=BAZ}} 
${FOO={${BAR=BAZ}} 

在第一种情况下,应该投其所好,但不包括最后} ,第二种情况应该完全匹配。这是因为我试图将两个字符的开头${与单字符关闭}匹配。开幕式和闭幕式都应该能够逃脱。

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

这可以用正则表达式来完成,还是我最好只写一个pyparsing解析器?

+0

你试图匹配的东西有点不清楚,特别是最后两个例子。为什么最后一个例子没有平衡大括号? – 2014-10-26 21:21:52

+0

第一个示例转义了两个字符的开头序列:我试图将'$ {'与'}'匹配,而不仅仅是{{和}}。在第一种情况下,整个匹配应该是'$ {FOO = \ $ {BAR = BAZ}',而第二种情况下整个字符串应该匹配,因为第二个'{'实际上并没有打开任何东西。 – 2014-10-26 21:23:33

+0

我编辑了这个问题,使其更清晰。 – 2014-10-26 21:24:52

回答

3

你可以试试这个模式:

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

online example

细节:

(?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)

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

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

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

你可以看到一个full explanation here.

关于原子团:

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

这里,原子组阻止catastrophic backtracking,如果模式后来失败,可能会出现这种结构(?:A+|B)+

+0

哇,那很整齐!什么,确切地说,是一个原子团体?我也不知道'(* SKIP)'或'(* FAIL)'。你能解释一下吗? – 2014-10-26 21:40:33