1

How do I parse and match calculation strings?

This is my code:

$pattern = '/(?:\d+(?:\.\d+)?\D)+\d+(?:\.\d+)/';
$input = [
    '1.0+2.5*5.4',
    '5*8-4'
];
foreach($input as $string){
    preg_match($pattern, $string, $match);
    print_r($match);
}

I can't figure out what I'm doing wrong.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
clarkk
  • 27,151
  • 72
  • 200
  • 340

3 Answers3

2

It's because of the greediness of "?", try "+" as in :

$pattern = '/(?:\d+(?:\.\d+)?\D)+\d+(?:\.\d+)/';
$input = '34.27-15.44/8.44';
echo $input."\n";
preg_match($pattern, $input, $match);
print_r($match);

As for your edit:

$pattern = '/(?:\d+(?:\.?\d+)?\D)+\d+/';
$input = [
    '1.0+2.5*5.4',
    '5*8-4',
    '5*8-4string',
    'string5*8-4',
    'string'
];
foreach($input as $string){
    preg_match($pattern, $string, $match);
    print_r($match);
}

It gives:

Array
(
    [0] => 1.0+2.5*5.4
)
Array
(
    [0] => 5*8-4
)
Array
(
    [0] => 5*8-4
)
Array
(
    [0] => 5*8-4
)
Array
(
)

as I suspect you want it to.

http://codepad.viper-7.com/4FoQnB

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Shikiryu
  • 10,180
  • 8
  • 49
  • 75
  • Have made your change.. Now it works with the first input.. The second input with integers doesn't work.. Have updated the question – clarkk Apr 01 '13 at 14:54
0

The result is expected, given your expression.

Note how \D can only match once (or nonce). \D has matched the -, so nothing else can match /.

You might be tempted to solve this by putting a quantifier on larger part of your expression, but let me anticipate the problems you'll face going that way and suggest that you use a loop that substitutes a single operand-operator-operand triplet at a time, quitting when no substitutions can be made. This is because no single-pass regular expression can properly manage order of operations (e.g. PEMDAS).

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Andrew Cheong
  • 29,362
  • 15
  • 90
  • 145
  • However, PCRE can very well match complex formulas, including operator precedence and parens. One can define named subpatterns and `(?R` recurse on them. It's not single-pass anymore, if you will. – mario Apr 01 '13 at 14:27
  • @mario - Good point; forgot about those regex extras. I don't disagree that matching and parsing can be done via pattern (single or looping). I think my caveat was more about the computation step, if there will be one. However I may be mistaken. The last time I answered such a question, it was [regarding reverse polish notation expressions](http://stackoverflow.com/questions/12293395/ruby-regexp-issue-involving-reverse-polish-calculator/12293553#12293553), where it seemed imperative that substitutions occurred in a loop, layer by layer. This may not apply to infix notation. – Andrew Cheong Apr 01 '13 at 14:35
0

Try this. It works for integers and floats:

 $pattern = '/(?:\d+(?:(\.|)\d*)?\D)*\d*(?:(\.|)\d+)/';
 $input =array(
   '1.0+2.5*5.4',
   '5*8-4',
   '5*8-4string'
 );
 foreach($input as $string){
    echo "input:".$string."\n";
    preg_match($pattern, $string, $match);
    print_r("output:".$match[0]."\n");
  }

Output:

 input:1.0+2.5*5.4
 output:1.0+2.5*5.4
 input:5*8-4
 output:5*8-4
 input:5*8-4string
 output:5*8-4
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
mohammad mohsenipur
  • 3,218
  • 2
  • 17
  • 22