递归正则表达式不起作用

时间:2012-10-12 17:17:57

标签: php regex

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

abc {def ghi {jkl mno} pqr stv} xy z

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

abc <tag>def ghi <tag>jkl mno</tag> pqr stv</tag> xy z

我试过

'#(?<!\pL)\{  ( ([^{}]+) | (?R) )*  \}(?!\pL)#xu'

但我得到的只是<tag>xy z</tag>。求助,我做错了什么?

2 个答案:

答案 0 :(得分:5)

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

$str = preg_replace('/\{/', '<tag>', $str);
$str = preg_replace('/\}/', '</tag>', $str);

另一种选择是仅替换匹配的{},但您必须重复执行此操作,因为对preg_replace的一次调用无法替换多个嵌套级别:

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

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

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

答案 1 :(得分:3)

两个步骤怎么样:

s!{!<tag>!g;
s!}!</tag>!g;

(perl格式;视情况转换为您的格式)

或者这个:

1 while s!{([^{}]*)}!<tag>$1</tag>!g;