-2

I have an arithmetic string that will be similar to the following pattern.

a. 1+2+3
b. 2/1*100
c. 1+2+3/3*100
d. (1*2)/(3*4)*100

Points to note are that
1. the string will never contain spaces.
2. the string will always be a combination of Numbers, Arithmetic symbols (+, -, *, /) and the characters '(' and ')'

I am looking for a regex in PHP to split the characters based on their type and form an array of individual string characters like below.
(Note: I cannot use str_split because I want numbers greater than 10 to not to be split.)

a. 1+2+3
output => [
0 => '1'
1 => '+'
2 => '2'
3 => '+'
4 => '3'
]

b. 2/1*100
output => [
0 => '2'
1 => '/'
2 => '1'
3 => '*'
4 => '100'
]`

c. 1+2+3/3*100

output => [
0 => '1'
1 => '+'
2 => '2'
3 => '+'
4 => '3'
5 => '/'
6 => '3'
7 => '*'
8 => '100'
]`

d. (1*2)/(3*4)*100

output => [
0 => '('
1 => '1'
2 => '*'
3 => '2'
4 => ')'
5 => '/'
6 => '('
7 => '3'
8 => '*'
9 => '4'
10 => ')'
11 => '*'
12 => '100'
]

Thank you very much in advance.

  • 2
    Welcome to StackOverflow. Unfortunately this is neither a tutorial site nor web search replacement. We however can can help solve [certain on-topic problems](https://stackoverflow.com/help/on-topic), but it's **your** job to [put some efforts](http://meta.stackoverflow.com/questions/261592) on the subject in the first place, including elementary [(re)search](https://google.com/). – Marcin Orlowski Aug 10 '17 at 07:47
  • Try to do it and provide the code, and I'll share with you an easier way to do that. – Sarkouille Aug 10 '17 at 07:50
  • 1
    You'd better write a parser for such job. – Toto Aug 10 '17 at 09:05

6 Answers6

2

Use this regex :
(?<=[()\/*+-])(?=[0-9()])|(?<=[0-9()])(?=[()\/*+-])

It will match every position between a digit or a parenthesis and a operator or a parenthesis.
(?<=[()\/*+-])(?=[0-9()]) matches the position with a parenthesis or an operator at the left and a digit or parenthesis at the right
(?<=[0-9()])(?=[()\/*+-]) is the same but with left and right reversed.

Demo here

Gawil
  • 1,171
  • 6
  • 13
  • Thank you. The regex seems to be working but when I run the below code - $re = '/(?<=[()\/*+-])(?=[0-9()])|(?<=[0-9()])(?=[()\/*+-])/m'; $str = '1+2+3'; preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0); I get the below output with blank values. array:4 [▼ 0 => array:1 [▼ 0 => "" ] 1 => array:1 [▼ 0 => "" ] ] I would like to have the array as (1, +, 2, +, 3). – Chaitra Hegde Aug 10 '17 at 09:14
  • Am I running it wrong. Thank you but I have rarely used regex before and cant get to work on them. – Chaitra Hegde Aug 10 '17 at 09:17
  • @ChaitraHegde I think you want to use the preg_split function, instead of preg_match_all. http://php.net/manual/fr/function.preg-split.php – Gawil Aug 10 '17 at 09:24
  • @ChaitraHegde By the way, take a look at ClasG's answer, it's simpler than mine. You'll have to add the PREG_SPLIT_NO_EMPTY flag however, to delete empty elements in the returned array. – Gawil Aug 10 '17 at 09:28
  • Thank you, preg_split turns out to be exactly the function I am looking for and with the regex you provided. – Chaitra Hegde Aug 10 '17 at 09:40
1

Since you state that the expressions are "clean", no spaces or such, you could split on

\b|(?<=\W)(?=\W)

It splits on all word boundaries and boundaries between non word characters (using positive lookarounds matching a position between two non word characters).

See an illustration here at regex101

SamWhan
  • 8,296
  • 1
  • 18
  • 45
0

As I said, I will help you with that if you can provide some work you did by yourself to solve that problem.

However, if when crafting an unidimensional array out of an arithmetic expression, your objective is to parse and cimpute that array, then you should build a tree instead and hierarchise it by putting the operators as nodes, the branches being the operands :

'(1*2)/(3*4)*100'

Array
(
    [operand] => '*',
    [left] => Array
        (
            [operand] => '/',
            [left] => Array
                (
                    [operand] => '*',
                    [left] => 1,
                    [right] => 2
                ),
            [right] => Array
                (
                    [operand] => '*',
                    [left] => 3,
                    [right] => 4
                )
        ),
    [right] => 100
)
Sarkouille
  • 1,275
  • 9
  • 16
0

There is no need to use regex for this. You just loop through the string and build the array as you want.

Edit, just realized it can be done much faster with a while loop instead of two for loops and if().

$str ="(10*2)/(3*40)*100";
$str = str_split($str); // make str an array

$arr = array();
$j=0; // counter for new array
for($i=0;$i<count($str);$i++){ 
    if(is_numeric($str[$i])){ // if the item is a number
        $arr[$j] = $str[$i]; // add it to new array 
        $k = $i+1;
        while(is_numeric($str[$k])){ // while it's still a number append to new array item.
            $arr[$j] .= $str[$k]; 
            $k++; // add one to counter.
            if($k == count($str)) break; // if counter is out of bounds, break loop.
        }
        $j++; // we are done with this item, add one to counter.
        $i=$k-1; // set new value to $i
    }else{
        // not number, add it to the new array and add one to array counter.
        $arr[$j] = $str[$i]; 
        $j++;
    }
}

var_dump($arr);

https://3v4l.org/p9jZp

Andreas
  • 23,610
  • 6
  • 30
  • 62
0

You can also use this matching regex: [()+\-*\/]|\d+

Demo

Egan Wolf
  • 3,533
  • 1
  • 14
  • 29
0

I was doing something similar to this for a php calculator demo. A related post.

Consider this pattern for preg_split():

~-?\d+|[()*/+-]~ (Pattern Demo)

This has the added benefit of allowing negative numbers without confusing them for operators. The first "alternative" matches positive or negative integers, while the second "alternative (after the |) matches parentheses and operators -- one at a time.

In the php implementation, I place the entire pattern in a capture group and retain the delimiters. This way no substrings are left behind. ~ is used as the pattern delimiter so that the slash in the pattern doesn't need to be escaped.

Code: (Demo)

$expression = '(1*2)/(3*4)*100+-10';
var_export(
    preg_split(
        '~(-?\d+|[()*/+-])~',
        $expression,
        0,
        PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
    )
);

Output:

array (
  0 => '(',
  1 => '1',
  2 => '*',
  3 => '2',
  4 => ')',
  5 => '/',
  6 => '(',
  7 => '3',
  8 => '*',
  9 => '4',
  10 => ')',
  11 => '*',
  12 => '100',
  13 => '+',
  14 => '-10',
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • @ChaitraHegde When comparing efficiency in terms of steps between the currently accepted answer and my pattern using your four test case strings, my method will result roughly 4x faster. This answer can be merged with my other post to allow decimals if you so desire. – mickmackusa Aug 21 '17 at 12:02