1

I'm struggling with some regular expressions. What I want to do is find money amounts in a string, remove the €,$, or £ but keep the number, and then look to see if there is a 'b' or an 'm' - in which case write 'million platinum coins' or 'million gold coin' respectively but otherwise just put 'gold coins'.

I have most of that as a hack (see below) with the small problem that my regex does not seem to work. The money amount comes out unchanged.

Desired behaviour examples

I intend to leave the decimal places and thousands separators as is

$12.6m ==> 12.6 million gold coins

£2b ==> 2 million platinum coins

€99 ==> 99 gold coins

My code

Here is my non-working code (I suspect my regex might be wrong).

protected function funnymoney($text){
    $text = preg_replace('/[€$£]*([0-9\.,]+)([mb])/i','\0 %\1%',$text);
    $text = str_replace('%b%','million platnum coins',$text);
    $text = str_replace('%m%','million gold coins',$text);
    $text = str_replace('%%','gold coins',$text);
    return $text;
}

I would greatly appreciate it if someone could explain to me what I am missing or getting wrong and guide me to the right answer. You may safely assume I know very little about regular expressions. I would like to understand why the solution works too if I can.

4 Answers 4

2

Using preg_replace_callback, you can do this in a single function call:

define ("re", '/[€$£]*(\.\d+|\d+(?:[.,]\d+)?)([mb]|)/i');

function funnymoney($text) {
   return preg_replace_callback(re, function($m) {
      return $m[1] .
        ($m[2] != "" ? " million" : "") . ($m[2] == "b" ? " platinum" : " gold") .
        " coins";
   }, $text);
}

// not call this function

echo funnymoney('$12.6m');
//=> "12.6 million gold coins"

echo funnymoney('£2b');
//=> "2 million platinum coins"

echo funnymoney('€99');
//=> "99 gold coins"
Sign up to request clarification or add additional context in comments.

Comments

1

I am not sure how you intend to handle decimal places and thousands separators, so that part of my pattern may require adjustment. Beyond that, match the leading currency symbol (so that it is consumed/removed, then capture the numeric substring, then capture the optional trailing units (b or m).

Use a lookup array to translate the units to English. When the unit character is missing, apply the fallback value from the lookup array.

A lookup array will make your task easier to read and maintain.

Code: (Demo)

$str = '$1.1m
Foo
£2,2b
Bar
€99.9';

$lookup = [
    'b' => 'million platinum coins',
    'm' => 'million gold coins',
    ''  => 'gold coins',
];

echo preg_replace_callback(
         '~[$£€](\d+(?:[.,]\d+)?)([bm]?)~iu',
         function($m) use ($lookup) {
             return "$m[1] " . $lookup[strtolower($m[2])];
         },
         $str
     );

Output:

1.1 million gold coins
Foo
2,2 million platinum coins
Bar
99.9 gold coins

1 Comment

I'm selecting this answer as it has the best chance of me being able to modify it later and understand it if I return to my code after a long break. Also, it worked like a charm.
1

Your regex has a first full match on the string, and it goes on index 0 of the returning array, but it seems you just need the capturing groups.

$text = preg_replace('/[€$£]*([0-9\.,]+)([mb])/i','\1 %\2%',$text);

Funny question, btw!

1 Comment

Thanks, I do weird things when I code for my own amusement. I'll put the end results online at some point if you want to look me up.
1

Is this what you want?

<?php

/**
    $12.6m ==> 12.6 million gold coins

    £2b ==> 2 million platinum coins

    €99 ==> 99 gold coins
 */

$str = <<<EOD
$12.6m

£2b

€99
EOD;

preg_match('/\$(.*?)m/', $str, $string1);
echo $string1[1] . " million gold coins \n";

preg_match('/\£(.*?)b/', $str, $string2);
echo $string2[1] . " million platinum coins \n";

preg_match('/\€([0-9])/', $str, $string3);
echo $string3[1] . " gold coins \n";

// output:
// 12.6 million gold coins
// 2 million platinum coins
// 9 gold coins

1 Comment

Not quite as I am looking for 9 possible combinations. However, your general approach would most likely work if slightly inelegantly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.