regex用括号递归包装

时间:2015-05-21 06:00:53

标签: php regex preg-replace

我正在尝试用括号包装表达式。表达式从一些数字数学开始,以一个单位结束,例如:

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]+$)/', '($1)', '4+(5+6)*3 meter')

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

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

我想知道是否有办法在数学部分尚未用括号括起来进行替换。表达式的第二个例子将使解决方案更难一点。

2 个答案:

答案 0 :(得分:1)

我下班后试过这个,我觉得这样可行。我的想法是通过删除壁橱匹配括号重复减少表达式,直到不再剩余。如果最终表达式非空,那么我们需要用括号包装原始表达式,否则我们不会。

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

  1. ((1 + 2)*(2 + 1))+ 1
  2. (*)+ 1
  3. 1
  4. 这里的最终值非空,所以我们扭曲表达式:(((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))
    

答案 1 :(得分:0)

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

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

修改

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

(1+2)*(3+4)

因为这意味着我们需要使用堆栈计算机跟踪打开和关闭括号(以及正则表达式不具备的功能),我认为没有正则表达式的解决方案。