最可靠方法是用正则非贪婪匹配单层括号内内容,如/(?(1*))/;嵌套场景需循环扫描计数或改用手动解析,PCRE递归易栈溢出不推荐。() ↩

用 preg_match_all 捕获括号内的内容最可靠
直接用 explode 或 str_split 分割括号会漏掉嵌套、忽略配对、无法提取内容——正则才是正确解法。核心是匹配成对括号之间的内容,但注意:PHP 的 PCRE 不支持无限递归,简单场景用非贪婪匹配即可。
常见错误是写成 /\((.*)\)/,结果跨括号全吞了(比如 (a(b)) 只捕获到 a(b)。应该用非贪婪+排除右括号:
-
/(?:只匹配一层无嵌套的括号,\([^()]*\))/ $matches['name']是完整括号串(含括号) -
/(?:捕获括号内纯内容(不含括号),\(([^()]*)\))/ $matches['content']是中间部分 - 若文本含中文括号或多种括号(
【】、「」),需在字符类里显式添加,如/\(([^()【】]*)\)/u并加u修饰符
处理多层嵌套括号只能靠循环或扩展库
PCRE 原生不支持通用嵌套匹配((?R) 在 PHP 中受限且易栈溢出),别硬刚正则。实际项目中更稳妥的做法是手动扫描:
- 遍历字符串,用计数器跟踪左括号减右括号数量,为 0 时截取一段完整括号块
- 遇到嵌套时,先提取最内层,再外层替换为占位符后重试(适合模板类场景)
- 真要正则解决,可用
ext/pcre的(?R),但必须确保输入可控,否则PREG_RECURSION_LIMIT_ERROR很容易触发
示例循环法片段:
立即学习“PHP免费学习笔记(深入)”;
$text = "func(a, inner(b, c), d)";
$stack = [];
$result = [];
for ($i = 0; $i < strlen($text); $i++) {
if ($text[$i] === '(') {
$stack[] = $i;
} elseif ($text[$i] === ')' && !empty($stack)) {
$start = array_pop($stack);
if (empty($stack)) {
$result[] = substr($text, $start + 1, $i - $start - 1);
}
}
}
preg_split 分割括号本身(不是内容)要小心空元素
如果目标只是把文本按括号字符切开(比如 "a(b)c" → ['a', 'b', 'c']),用 preg_split 更轻量,但默认会保留空字符串和括号位置信息:
-
preg_split('/[()]/', $text)返回['a', 'b', 'c'],但若开头结尾是括号(如"(a)"),会得到['', 'a', ''] - 加
PREG_SPLIT_NO_EMPTY过滤空项:preg_split('/[()]/', $text, -1, PREG_SPLIT_NO_EMPTY) - 若还需知道分割点是左括号还是右括号,改用
preg_match_all('/([()])/', $text, $m)拿位置,再手动切片
中文括号、全角符号、转义字符容易被忽略
用户输入不可信时,( 和 ((U+FF08)是两个不同字符,正则不写全就会漏。还有转义场景:文本里出现 \(literal\) 应该跳过。
- 匹配全类型括号:用 Unicode 属性
/\p{Ps}|\p{Pe}/u(需u修饰符),但性能略低;更常用的是显式枚举:/[()()【】〔〕《》「」『』]/u - 跳过已转义的括号:正则加负向先行断言,如
/(?,但注意 PHP 字符串里反斜杠要双写 - 测试时务必覆盖边界:空括号
()、连续括号(())、括号在行首/尾、含换行符的括号块
真正难的从来不是写对一行正则,而是想清楚括号的语义:它代表分组?参数列表?注释?还是用户随意打的标点?规则没理清,正则越写越错。











