如何编写与嵌套括号匹配的递归正则表达式?

时间:2013-12-13 14:49:45

标签: php regex

我正在尝试编写一个匹配嵌套括号的正则表达式,例如:

"(((text(text))))(text()()text)(casual(characters(#$%^^&&#^%#@!&**&#^*!@#^**_)))"

这样的字符串应该匹配,因为所有嵌套的括号都是关闭的,而不是:

"(((text)))(text)(casualChars*#(!&#*(!))"

不应该,或者更好,应该至少匹配第一个“(((text)))(text)”部分。

实际上,我的正则表达式是:

 $regex = '/( (  (\() ([^[]*?)  (?R)?  (\))  ){0,}) /x';

但它并没有像我期待的那样正常工作。如何解决?我哪里错了?谢谢!

2 个答案:

答案 0 :(得分:12)

此模式有效:

$pattern = '~ \( (?: [^()]+ | (?R) )*+ \) ~x';

括号内的内容只是描述:

“所有不是括号或递归(=其他括号)”x 0或更多次

如果要捕获括号内的所有子串,必须将此模式放在前瞻中以获取所有重叠结果:

$pattern = '~(?= ( \( (?: [^()]+ | (?1) )*+ \) ) )~x';
preg_match_all($pattern, $subject, $matches);
print_r($matches[1]);

请注意,我添加了一个捕获组,我已将(?R)替换为(?1)

(?R) -> refers to the whole pattern (You can write (?0) too)
(?1) -> refers to the first capturing group

这个先行技巧是什么?

前瞻(或后瞻)中的子模式与任何内容都不匹配,它只是一个断言(测试)。因此,它允许多次检查相同的子字符串。

如果显示整个模式结果(print_r($matches[0]);),您将看到所有结果都是空字符串。获取前瞻中子模式所找到的子串的唯一方法是将子模式包含在捕获组中。

注意:递归子模式可以像这样改进:

\( [^()]*+ (?: (?R) [^()]* )*+ \)

答案 1 :(得分:2)

当我找到这个答案时,我无法弄清楚如何修改模式以使用我自己的分隔符{}。所以我的方法是让它更通用。

这是一个用自己的变量左右分隔符生成正则表达式模式的脚本

$delimiter_wrap  = '~';
$delimiter_left  = '{';/* put YOUR left delimiter here.  */
$delimiter_right = '}';/* put YOUR right delimiter here. */

$delimiter_left  = preg_quote( $delimiter_left,  $delimiter_wrap );
$delimiter_right = preg_quote( $delimiter_right, $delimiter_wrap );
$pattern         = $delimiter_wrap . $delimiter_left
                 . '((?:[^' . $delimiter_left . $delimiter_right . ']++|(?R))*)'
                 . $delimiter_right . $delimiter_wrap;

/* Now you can use the generated pattern. */
preg_match_all( $pattern, $subject, $matches );