Reputation: 23552
I like to convert string with a price to a float value. The price comes from different languages and countries and can look like this:
1,00 €
€ 1.00
1'000,00 EUR
1 000.00$
1,000.00$
1.000,00 EURO
or whatever you can think of...
Not sure I got the full range of possibilities with my examples. I am also not sure if it is possible to make in international convert blindly, maybe I have to use a language code? So for the start Euro and Dollar would be enough.
floatval()
is kind of stupid so I need something more here. I think I should first remove all chars beside numbers, ,
and .
. Then fix the ,
/ .
and use floatval finally.
Has someone done this before and can help me a little?
I would prefer a solution without regexp ;)
Update: Current solution with tests https://onlinephp.io/c/9e95e
Upvotes: 9
Views: 21901
Reputation: 1
I got most of the way with the help of this particular thread, however I was still able to generate edge cases.
The main issue is the position of the decimal point and comma across currencies, so performing incremental checks is needed.
//remove everything except for numbers, decimals, commas and hyphens
$value = preg_replace('/[^0-9.,-]+/', '', $value);
$decimal = strpos($value, '.');
$comma = strpos($value, ',');
//check the 3rd last character
if(!in_array(substr($value, -3, 1), [".", ","])) {
if($comma && (substr($value, -3, 1) != ".")) {
$value .= ".00";
} elseif($decimal && (substr($value, -3, 1) != ",")) {
$value .= ",00";
}
}
$decimal = strpos($value, '.');
$comma = strpos($value, ',');
if($comma === false) //no comma must be a decimal number already
return (float) $value;
if($decimal < $comma){ //decimal before a comma = euro
$value = str_replace(['.',','], ['','.'], $value);
return (float) $value;
}
//comma first = traditional thousan separator
$value = str_replace(',', '', $value);
return (float)$value;
Upvotes: 0
Reputation: 23552
Ok, I tried it myself. What do you think of this?
function priceToFloat($s){
// is negative number
$neg = strpos((string)$s, '-') !== false;
// convert "," to "."
$s = str_replace(',', '.', $s);
// remove everything except numbers and dot "."
$s = preg_replace("/[^0-9\.]/", "", $s);
// remove all seperators from first part and keep the end
$s = str_replace('.', '',substr($s, 0, -3)) . substr($s, -3);
// Set negative number
if( $neg ) {
$s = '-' . $s;
}
// return float
return (float) $s;
}
Here some tests: https://onlinephp.io/c/9e95e
Sorry. I couldn't include the other functions because codepad did not like them. But I compared them and there was trouble with strings like "22 000,76" or "22.000"
Update: As Limitless isa pointed out you might have a look at the build in function money-format.
Upvotes: 21
Reputation: 31
not perfect, but it work
function priceToFloat($s){
// clear witespaces
$s = trim($s);
$s = str_replace(' ', '', $s);
// is it minus value
$is_minus = false;
if(strpos($s, '(') !== false)
$is_minus = true;
if(strpos($s, '-') !== false)
$is_minus = true;
// check case where string has "," and "."
$dot = strpos($s, '.');
$semi = strpos($s, ',');
if($dot !== false && $semi !== false){
// change fraction sign to #, we change it again later
$s = str_replace('#', '', $s);
if($dot < $semi) $s = str_replace(',','#', $s);
else $s = str_replace('.','#', $s);
// remove another ",", "." and change "#" to "."
$s = str_replace([',','.', '#'], ['','', '.'], $s);
}
$s = str_replace(',', '.', $s);
// clear usless elements
$s = preg_replace("/[^0-9\.]/", "", $s);
// if it minus value put the "-" sign
if($is_minus) $s = -$s;
return (float) $s;
}
working cases
$prices = [
'123.456,789',
'123,456.789',
'123 456,789',
'123 456.789',
'-123,456.789',
'(123,456.789)',
];
foreach($prices as $price)
echo priceToFloat($price).'<br />';
return
123456.789
123456.789
123456.789
123456.789
-123456.789
-123456.789
Upvotes: 1
Reputation: 3802
price to number number to price examples
<?php
$number="1.050,50";
$result=str_replace(',','.',str_replace('.','',$number));
echo $result. "<br/>";
// 1050.50
setlocale(LC_MONETARY, 'tr_TR');
echo money_format('%!.2n', $result) ;
// 1.050,50
?>
Upvotes: 1
Reputation:
This function will fix your problem:
function priceToSQL($price)
{
$price = preg_replace('/[^0-9\.,]*/i', '', $price);
$price = str_replace(',', '.', $price);
if(substr($price, -3, 1) == '.')
{
$price = explode('.', $price);
$last = array_pop($price);
$price = join($price, '').'.'.$last;
}
else
{
$price = str_replace('.', '', $price);
}
return $price;
}
Upvotes: 2
Reputation: 6346
To remove all but numbers, commas and full stops:
<?php
$prices = array( "1,00 €",
"€ 1.00",
"1'000,00 EUR",
"1 000.99$",
"1,000.01$",
"1.000,10 EURO");
$new_prices = array();
foreach ($prices as $price) {
$new_prices[] = preg_replace("/[^0-9,\.]/", "", $price);
}
print_r($new_prices);
Output:
Array ( [0] => 1,00 [1] => 1.00 [2] => 1000,00 [3] => 1000.99 [4] => 1,000.01 [5] => 1.000,10 )
Now lets utilize the parseFloat function from Michiel - php.net (I won't paste it here since it's a pretty big function):
<?php
$prices = array( "1,00 €",
"€ 1.00",
"1'000,00 EUR",
"1 000.99$",
"1,000.01$",
"1.000,10 EURO");
$new_prices = array();
foreach ($prices as $price) {
$new_prices[] = parseFloat(preg_replace("/[^0-9,\.]/", "", $price));
}
print_r($new_prices);
Output will be:
Array ( [0] => 1 [1] => 1 [2] => 1000 [3] => 1000.99 [4] => 1000.01 [5] => 1000.1 )
Upvotes: 1
Reputation: 34867
Removing all the non-numeric characters should give you the price in cents. You can then divide that by 100 to get the 'human readable' price. You could do this with something like the filter_var FILTER_SANITIZE_NUMBER_INT. For example:
$cents = filter_var($input, FILTER_SANITIZE_NUMBER_INT);
$price = floatval($cents / 100);
Above is untested, but something like that is probably what you're looking for.
Upvotes: 2