正则表达式以递归方式用括号括起来

regex to recursively wrap with parentheses

我正在尝试用括号括起表达式。表达式以一些数字 math 开头,以一个单位结尾,例如:

4+(5+6)*3 meter
(23+4)*3*(76+5) second

我想要的结果是:

(4+(5+6)*3) meter
((23+4)*3*(76+5)) second

问题是该函数被递归调用,并且只有在 preg_replace 之后的字符串中没有变化 时才会停止,因此进行以下尝试:

preg_replace('/(.+)(?=\s+[a-z]+$)/', '()', '4+(5+6)*3 meter')

永远不会停止,结果将是:

(4+(5+6)*3) meter
((4+(5+6)*3)) meter
(((4+(5+6)*3))) meter
etc..

我想知道是否有一种方法可以仅在数学部分尚未用括号括起来的情况下进行替换。第二个表达式示例会使解决方案更难一些。

您需要确保您的第一个捕获组添加了不在括号内的约束:

preg_replace('/^([^(].+?[^)])(?=\s+[a-z]+$)/', '()', '4+(5+6)*3 meter')

编辑:

如@Talvir 所述,这在以下情况下不起作用:

(1+2)*(3+4)

因为这意味着我们需要用堆栈机器跟踪左括号和右括号(正则表达式无法做到),我认为正则表达式没有解决方案。

我下班后尝试了这个,我认为这会奏效。我的想法是通过删除壁橱匹配括号来重复减少表达式,直到没有剩余为止。如果最终表达式不为空,那么我们需要用括号将原始表达式括起来,否则我们不需要。

例如,如果表达式是 ((1+2)*(2+1))+1,则缩减如下:

  1. ((1+2)*(2+1))+1
  2. (*)+1
  3. +1

这里的最终值是非空的,所以我们扭曲表达式:(((1+2)*(2+1))+1).

代码如下:

$input = $output = '(23+4)*3*(76+5) meter';
// Split into arithmetic expression and the unit string bit
if (preg_match('/^(.+?)\s*([a-z]+)$/', $input, $match)) {
    $exp = $match[1];
    $unit = $match[2];

    // This is the main logic
    // Reduce the expression by repetitively removing closet matching parenthesis 
    $reduced_exp = $exp;
    do {
        // The fifth parameter $count returns the number replacements done
        $reduced_exp = preg_replace('/\([^()]+\)/', '', $reduced_exp, -1, $count);
    } while ($count); // Exit the loop if there are zero replacements

    // If reduced expression is non-empty then we need to wrap it with the parenthesis
    if (!empty($reduced_exp)) {
        $output = '('.$exp.') '.$unit;
    }
}
print_r($output); // Outputs ((23+4)*3*(76+5))