1

I am trying to figure out, how to remove surrounding brackets in math expressions using php.

Some cases are:
(A+B)(B+C) should stay the same
((((A)))) should get A
((A
(B+C))) should get A*(B+C)
(((((B+C)*A)))) should get (B+C)*A

I cannot find a solution which is right in any case. Using math rules like distributive property is no option.

I am not looking for copy-and-paste algorithm, just a criterion which fits all of my cases. This is the latest attempt, I tried different methods like regex, but I did not figure it out.

function removeSurroundingBrackets($str)
{
$res=$str;


if(strcmp($res[0],'(')===0 && strcmp($res[strlen($res)-1],')')===0)
{

    $firstOther=0;
    for(; $firstOther<strlen($str);$firstOther++)
    {
        if(strcmp($str[$firstOther],'(')!==0)
            break;
    }

    $removableCount=0;
    $removableCount=substr_count($str,')',$firstOther)-substr_count($str,'(',$firstOther);
}
return substr($str,$removableCount,-$removableCount);
}

EDIT: I found a Solution:

function removeSurroundingBrackets($str)
{
    $res=$str;


    while(strcmp($res[0],'(')===0 && strcmp($res[strlen($res)-1],')')===0)
    {
        if($this->checkBrackets(substr($res,1,-1)))
            $res=substr($res,1,-1);
        else
            return $res;

    }
    return $res;
}
function checkBrackets($str)
{
    $currdepth=0;
    foreach(str_split($str) as $char)
    {
        if(strcmp($char,')')===0)
        {
            if($currdepth<=0)
                return false;
            else
                $currdepth--;
        }
        else if(strcmp($char,'(')===0)
            $currdepth++;
    }
    return true;
}
Martin B
  • 61
  • 1
  • 4
  • 1
    Show us please what did you have tried now. Which way would yo go to remove the brackets? Then the community will help you. You can do this with regex, or string extraction or or or ... – FrankTheTank_12345 Jun 26 '17 at 07:28
  • With regex you can try [something like this demo](https://eval.in/822350). – bobble bubble Jun 26 '17 at 09:16
  • @bobblebubble: that's a possibility, but instead of using `while` and `preg_match`, you should use `do...while` and the count parameter of `preg_replace`. – Casimir et Hippolyte Jun 26 '17 at 09:31
  • 1
    @CasimiretHippolyte Yes, good idea thank you! Have not thought of the $count. So you can try [like this other demo @MartinB](https://eval.in/822646). – bobble bubble Jun 26 '17 at 17:52

3 Answers3

3

With preg_match:

$pattern = '~\A(?:\((?=([^()]*(?:\((?1)\)[^()]*)*)(\)\2?+)\z))*\K(?(1)\1|.*)~';
if ( preg_match($pattern, $str, $m) ) 
    echo $m[0], PHP_EOL;

The idea is to consume parenthesis at the start of the string as long as they are outermost parenthesis. To be sure that they are outermost parenthesis, you need to check if there's always a well balanced expression inside them.

To consume/count these outermost parenthesis, I use this design:

\A # from the start of the string
(?: # an eventually repeated non-capturing group
    \(
    (?= # a lookahead to check if the corresponding closing parenthesis exists
        ([^()]*(?:\((?1)\)[^()]*)*) # balanced expression inside
        ( \) \2?+ ) # capture group grows at each non-capturing group repetition
        \z # anchored at the end of the string
    )
)* # since the quantifier is greedy, it will consume all opening parenthesis

Then, you only need to use \K to remove these parenthesis from the match result and to test if the capture group 1 exists:

\K
(?(?1)  # if the capture group 1  exists
    \1  # match its content
     |  # else
     .* # match all the string
)
Casimir et Hippolyte
  • 83,228
  • 5
  • 85
  • 113
  • 1
    @revo: thanks, but to be honest, it comes from Qtax answer: https://stackoverflow.com/questions/17039670/vertical-regex-matching-in-an-ascii-image – Casimir et Hippolyte Jun 26 '17 at 16:24
0
function removeSurroundingBrackets($str)
{
$res=$str;


while(strcmp($res[0],'(')===0 && strcmp($res[strlen($res)-1],')')===0)
{
    if($this->checkBrackets(substr($res,1,-1)))
        $res=substr($res,1,-1);
    else
        return $res;

}
return $res;
}
function checkBrackets($str)
{
$currdepth=0;
foreach(str_split($str) as $char)
{
    if(strcmp($char,')')===0)
    {
        if($currdepth<=0)
            return false;
        else
            $currdepth--;
    }
    else if(strcmp($char,'(')===0)
        $currdepth++;
}
return true;
}
Martin B
  • 61
  • 1
  • 4
0

How about a one-liner regex solution:

$re = "~(?|\({2,}". ( $p = "(\(((?:[^()]*|(?1))*)\))" ) ."\){2,}|^(((\([^()]+\))+))$|$p)~";
echo preg_replace($re, '$2', $str);

Live demo

revo
  • 43,830
  • 14
  • 67
  • 109