Search code examples
formattingnumbersdecimalpreg-replacestring-formatting

Formatting decimal output when multiplying all numerical values in a string?


I have this case where I have to multiply the numbers of a string but some of the values are in EUR and some are percentage. I made it this far as code goes:

    $string = "description: test1: 10 €, test2: 7.50 €, test3: 25%, test4: 30%";
    $levelamt = 0.75;

$description = preg_replace_callback('/([0-9]+)\s*(\s€|%)/i', function($matches) use ($levelamt){
   return ($matches[1] * $levelamt).$matches[2];
}, $string);

echo $description;

But it outputs this:

description: test1: 7.5 €, test2: 7.37.5 €, test3: 18.75%, test4: 22.5%

How should I modify the regular expression to multiply decimals and round the results of numbers? I want output to be like so:

description: test1: 7.50 €, test2: 5.63 €, test3: 18.75%, test4: 22.5%

So when it is € value to format it XX.YY € and when it is percentage value to format it XX.YY% when percentage is a hundreds and XX.Y% when percentage decimal is a tenth. I tried rounding. Maybe I do not put it in the right place. I also tried replacing [0-9] part of the regex to find only decimals but this brings in other problems. A bit stuck here. Any help appreciated! Thanks!


Solution

  • You may use

    $string = "description: test1: 10 €, test2: 7.50 €, test3: 25%, test4: 30%";
    $levelamt = 0.75;
    
    $description = preg_replace_callback('/(\d+(?:\.\d+)?)(\s*[€%])/i', function($matches) use ($levelamt){
       return number_format(round(($matches[1] * $levelamt), 2), 2).$matches[2];
    }, $string);
    
    echo $description;
    // => description: test1: 7.50 €, test2: 5.63 €, test3: 18.75%, test4: 22.50%
    

    See the PHP demo

    The regex will match

    • (\d+(?:\.\d+)?) - Group 1: one or more digits followed with an optional sequence of a . followed with 1+ digits
    • (\s*[€%]) - Group 2: 0+ whitespaces followed with or %.

    The round function will round the result of the multiplication and number_format will format the number as you need, with 2 digits after the decimal separator.