如何根据BODMAS规则提取特定括号内内容?现有正则仅支持单对括号
提取嵌套数学表达式中最内层第一个闭合括号的内容
嘿,我懂你的痛点——你原来用的非贪婪正则 preg_match('#\((.*?)\)#', $text, $match) 只会匹配从第一个左括号到第一个右括号的内容,在你的例子里会拿到 2+(3+(5-6,完全不是你要的 5-6。问题出在普通正则不会处理嵌套括号的层级,而你需要的是遵循BODMAS规则的最内层(第一个闭合的)括号内容。
下面给你两种实用的解决方案:
方案一:用简单正则精准匹配最内层括号
如果你的场景是标准数学表达式(括号内不会有转义括号或者其他特殊括号),直接用这个正则就能搞定:
$text = '1+(2+(3+(5-6)))+(7-8)'; // 匹配内部不含任何括号的括号对,也就是最内层的第一个 preg_match('#\(([^()]+)\)#', $text, $match); echo $match[1]; // 输出: 5-6
这个正则的逻辑很直接:找 ( 后面跟着完全没有括号的内容,再跟着 ),这样就精准定位了最内层的第一个闭合括号对。如果表达式里有多个最内层括号,它会优先匹配第一个出现的,正好符合你的需求。
方案二:手动遍历计数(复杂场景更可靠)
要是你需要处理更复杂的情况(比如表达式里有特殊字符,或者担心正则的边界情况),手动遍历字符串计数括号是更稳妥的选择:
$text = '1+(2+(3+(5-6)))+(7-8)'; $openBrackets = 0; $targetContent = ''; $lastOpenPos = -1; for ($i = 0; $i < strlen($text); $i++) { $char = $text[$i]; if ($char === '(') { $openBrackets++; // 记录当前左括号的位置,下一个右括号对应的就是它 $lastOpenPos = $i; } elseif ($char === ')') { $openBrackets--; // 遇到第一个闭合的括号对时,提取内容并停止遍历 if ($lastOpenPos !== -1) { $targetContent = substr($text, $lastOpenPos + 1, $i - $lastOpenPos - 1); break; } } } echo $targetContent; // 输出: 5-6
这个方法的思路是:遍历字符串时,每遇到左括号就计数加一并记录位置,遇到右括号就计数减一。当我们遇到第一个右括号时,对应的就是最近的那个左括号——这正是最内层的括号对,提取中间的内容就搞定了。
为什么原来的正则失效?
你原来的 #\((.*?)\)# 用了非贪婪匹配,它只会按顺序找第一个 ( 和第一个 ),完全不管中间的嵌套层级。在你的例子里,第一个 ) 是 (5-6) 的右括号,但非贪婪匹配会从最外层的 ( 开始匹配,所以拿到的是 2+(3+(5-6,这显然不符合你的需求。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

