Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manipulate a string so I can make implicit multiplication explicit in a math expression?

I want to manipulate a string like "...4+3(4-2)-...." to become "...4+3*(4-2)-....", but of course it should recognize any number, d, followed by a '(' and change it to 'd*('. And I also want to change ')(' to ')*(' at the same time if possible. Would nice if there is a possibility to add support for constants like pi or e too.

For now, I just do it this stupid way:

private function make_implicit_multiplication_explicit($string)
{
    $i=1;
    if(strlen($string)>1)
    {
        while(($i=strpos($string,"(",$i))!==false)
        {
            if(strpos("0123456789",substr($string,$i-1,1))) 
            {
                $string=substr_replace($string,"*(",$i,1);
                $i++;
            }
            $i++;
        }

        $string=str_replace(")(",")*(",$string);
    }        
    return $string;
 }

But I Believe this could be done much nicer with preg_replace or some other regex function? But those manuals are really cumbersome to grasp, I think.

like image 892
Nick Sick Avatar asked Dec 15 '22 21:12

Nick Sick


2 Answers

Let's start by what you are looking for:

  • either of the following: ((a|b) will match either a or b)
    • any number, \d
    • the character ): \)
  • followed by (: \(

Which creates this pattern: (\d|\))\(. But since you want to modify the string and keep both parts, you can group the \( which results in (\() making it worse to read but better to handle.

Now everything left is to tell how to rearrange, which is simple: \\1*\\2, leaving you with code like this

$regex = "/(\d|\))(\()/";
$replace = "\\1*\\2";
$new = preg_replace($regex, $replace, $test);

To see that the pattern actually matches all cases, see this example.

like image 134
kero Avatar answered Dec 17 '22 09:12

kero


To recognize any number followed by a ( OR a combination of a )( and place an asterisk in between them, you can use a combination of lookaround assertions.

echo preg_replace("/

         (?<=[0-9)])   # look behind to see if there is: '0' to '9', ')'
         (?=\()        # look ahead to see if there is: '('

         /x", '*', '(4+3(4-2)-3)(2+3)'); 

The Positive Lookbehind asserts that what precedes is either a number or right parentheses. While the Positive Lookahead asserts that the preceding characters are followed by a left parentheses.

Another option is to use the \K escape sequence in replace of the Lookbehind. \K resets the starting point of the reported match. Any previously consumed characters are no longer included ( throws away everything that it has matched up to that point. )

echo preg_replace("/

         [0-9)]   # any character of: '0' to '9', ')'
          \K      # resets the starting point of the reported match
          (?=\()  # look ahead to see if there is: '('

         /x", '*', '(4+3(4-2)-3)(2+3)'); 
like image 41
hwnd Avatar answered Dec 17 '22 10:12

hwnd