递归正则表达式不起作用

tij*_*agi 4 php regex

我工作的字符串看起来像这样:

abc {def ghi {jkl mno} pqr stv} xy z
Run Code Online (Sandbox Code Playgroud)

我需要在标签中放置数字括号,所以它应该是这样的

abc <tag>def ghi <tag>jkl mno</tag> pqr stv</tag> xy z
Run Code Online (Sandbox Code Playgroud)

我试过了

'#(?<!\pL)\{  ( ([^{}]+) | (?R) )*  \}(?!\pL)#xu'
Run Code Online (Sandbox Code Playgroud)

但我得到的只是<tag>xy z</tag>.请帮忙,我做错了什么?

Mar*_*der 5

根据定义,嵌套结构对于正则表达式来说太复杂了(是的,PCRE支持递归,但这对于这个替换问题没有帮助).有两种可能的选择(无论如何使用正则表达式).首先,您可以通过打开标签来简单地替换左括号,对于关闭标签则可以相同.但是,这也将转换不匹配的括号:

$str = preg_replace('/\{/', '<tag>', $str);
$str = preg_replace('/\}/', '</tag>', $str);
Run Code Online (Sandbox Code Playgroud)

另一种选择是只替换匹配{},但你必须这样做反复,因为一个呼叫preg_replace不能代替多个嵌套层次:

do
{
    $str = preg_replace('/\{([^{]*?)\}/', '<tag>$1</tag>', $str, -1, $count);
}
while ($count > 0)
Run Code Online (Sandbox Code Playgroud)

编辑:虽然PCRE支持递归,(?R)但这很可能对替换没有帮助.原因是,如果重复捕获组,其引用将仅包含最后一次捕获(即匹配/(a|b)+/aaaab,$1将包含b).我想这对于递归来说是一样的.这就是为什么你只能替换最里面的匹配,因为它是递归中捕获组的最后一个匹配.同样,您无法尝试捕获{}使用递归并替换它们,因为它们也可能被匹配任意次数,并且只会替换最后一个匹配.

只需匹配正确的嵌套语法然后替换最里面或最外面的匹配括号也无济于事(一次preg_replace调用),因为多个匹配永远不会重叠(因此,如果找到3个嵌套括号,则内部2个括号本身将被忽略进一步的比赛).