Неформатировать деньги при разборе в PHP

Есть ли способ получить значение с плавающей запятой для такой строки: 75,25 €, кроме parsefloat(str_replace(',', '.', $var))?

Я хочу, чтобы это зависело от текущего языка сайта, а иногда запятая могла быть заменена точкой.


person cili    schedule 28.02.2011    source источник
comment
Ну, с регулярным выражением будут проблемы с локализацией.   -  person Hamish    schedule 28.02.2011
comment
вы можете использовать решение, прикрепленное по адресу stackoverflow.com/questions/7407946/   -  person mcuadros    schedule 04.11.2013


Ответы (8)


Вы можете использовать

Пример из руководства:

$formatter = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
var_dump($formatter->parseCurrency("75,25 €", $curr));

дает: float(75.25)

Обратите внимание, что расширение intl не включено по умолчанию. См. Инструкции по установке.

person Gordon    schedule 28.02.2011
comment
+1 Это отличный способ сделать это, но только для PHP 5.3. Это может быть огромным ограничением - person Ben; 28.02.2011
comment
@Ben Поддержка PHP5.2 подошла к концу. Кроме того, расширение intl доступно в PECL до PHP 5.3, поэтому версия не имеет значения. - person Gordon; 28.02.2011
comment
@VladislavRastrusny, что написано в руководстве: параметр для получения названия валюты (3-буквенный код валюты ISO 4217). Важным словом здесь является получение. Он передается по ссылке. Думайте, что $matches в preg_match. - person Gordon; 28.07.2015
comment
Что такое $curr в вашем примере? - person Adam; 12.09.2016
comment
@Adam строка, обозначающая валюту; как доллары США или евро. - person Gordon; 12.09.2016
comment
NumberFormatter::DECIMAL, если строка представляет собой просто сумму без символа валюты. - person Fr0zenFyr; 26.05.2021

Это немного более сложное/медленное решение, но работает со всеми локалями. Решение @rlenom работает только с точками в качестве десятичного разделителя, а в некоторых локалях, например в испанском, в качестве десятичного разделителя используется запятая.

<?php

public function getAmount($money)
{
    $cleanString = preg_replace('/([^0-9\.,])/i', '', $money);
    $onlyNumbersString = preg_replace('/([^0-9])/i', '', $money);

    $separatorsCountToBeErased = strlen($cleanString) - strlen($onlyNumbersString) - 1;

    $stringWithCommaOrDot = preg_replace('/([,\.])/', '', $cleanString, $separatorsCountToBeErased);
    $removedThousandSeparator = preg_replace('/(\.|,)(?=[0-9]{3,}$)/', '',  $stringWithCommaOrDot);

    return (float) str_replace(',', '.', $removedThousandSeparator);
}

Тесты:

['1,10 USD', 1.10],
['1 000 000.00', 1000000.0],
['$1 000 000.21', 1000000.21],
['£1.10', 1.10],
['$123 456 789', 123456789.0],
['$123,456,789.12', 123456789.12],
['$123 456 789,12', 123456789.12],
['1.10', 1.1],
[',,,,.10', .1],
['1.000', 1000.0],
['1,000', 1000.0]

Предостережения: сбой, если десятичная часть содержит более двух цифр.

Это реализация из этой библиотеки: https://github.com/mcuadros/currency-detector.

person mcuadros    schedule 04.11.2013
comment
Гениальное решение, спасибо. Тестовые примеры приветствуются - person Bojangles; 04.11.2013
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013
comment
+1 за это решение, у меня работает даже с франко-канадскими номерами: $fr_num = 180 000,08 $; возвращает: двойной (180000,08) - person Steven Leggett; 15.01.2014
comment
Точки внутри классов символов экранировать не нужно. - person mickmackusa; 12.09.2018
comment
вы люди забыли об отрицательных суммах? - person Alexandru Trandafir Catalin; 25.05.2020
comment
Мне пришлось проверить, начинается ли строка с - и в этом случае умножить результат на -1, чтобы изменить его знак, иначе это преобразует все отрицательные числа в положительные. - person Alexandru Trandafir Catalin; 25.05.2020

использовать ereg_replace

$string = "$100,000";
$int = ereg_replace("[^0-9]", "", $string); 
echo $int;

выходы

1000000

function toInt($str)
{
    return (int)preg_replace("/\..+$/i", "", preg_replace("/[^0-9\.]/i", "", $str));
}

Обновить


<?php
$string = array("$1,000,000.00","$1 000 000.00","1,000 000.00","$123","$123 456 789","0.15¢");
foreach($string as $s) {
    echo $s . " = " . toInt($s) . "\n"; 
}
function toInt($str)
{
    return preg_replace("/([^0-9\\.])/i", "", $str);
}
?>

Результаты

$1,000,000.00 = 1000000.00
$1 000 000.00 = 1000000.00
1,000 000.00 = 1000000.00
$123 = 123
$123 456 789 = 123456789
0.15¢ = 0.15

и если вы примете его как целое число

<?php
$string = array("$1,000,000.00","$1 000 000.00","1,000 000.00","$123","$123 456 789","0.15¢");
foreach($string as $s) {
    echo $s . " = " . _toInt($s) . "\n";    
}
function _toInt($str)
{
    return (int)preg_replace("/([^0-9\\.])/i", "", $str);
}
?>

выходные данные

$1,000,000.00 = 1000000
$1 000 000.00 = 1000000
1,000 000.00 = 1000000
$123 = 123
$123 456 789 = 123456789
0.15¢ = 0

Итак, у вас есть это. одна строка, одна замена. ты можешь идти.

person rlemon    schedule 13.09.2011
comment
Большое спасибо. Я надеялся, что смогу обойтись без использования регулярных выражений, но это не критичная к производительности задача, так что все в порядке. Кроме того, ereg_replace() устарел — вместо этого используйте preg_replace() :-) - person Bojangles; 14.09.2011
comment
Возможно, вы захотите добавить . в регулярное выражение, иначе $100.00 будет равно 10000. - person Rocket Hazmat; 14.09.2011
comment
извините, я слишком привык к 5-2 дням. - person rlemon; 14.09.2011
comment
В основном для справки, у меня есть эта функция. Есть ли способ сжать его в одну строку? - person Bojangles; 14.09.2011
comment
@JamWaffles, да .. вот оно. - person rlemon; 14.09.2011
comment
Извините за двусмысленность; Я имел в виду, есть ли способ получить незначительное увеличение скорости, сократив его до одного фрагмента регулярного выражения, но все равно спасибо! - person Bojangles; 14.09.2011
comment
@JamWaffles, хе-хе, я подумал, что вы это имели в виду, я просто хотел дать буквальный ответ. Я посмотрю позже. Время уходить! - person rlemon; 14.09.2011
comment
разве это не должно быть ([^0-9\.]) вместо ([^0-9\\.]) ? В противном случае `\` не будет совпадать и останется в последней строке. - person bruchowski; 02.08.2013
comment
@bruchowski tehplayground.com/#YLwjbmpFm выглядит нормально. Я думаю, что вы можете быть технически правы, однако это старый ответ, и я не могу вспомнить свое мышление, связанное с регулярным выражением. - person rlemon; 02.08.2013
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013
comment
Отличный ответ! Тай @rlemon :) - person Andrew; 07.12.2013
comment
решение mcuadros работает для большего количества локалей, но спасибо за ваш вклад - person Cedric; 14.11.2015
comment
Возможно, вы захотите обновить свой ответ, поскольку Function ereg_replace() устарело. - person lzoesch; 23.12.2016
comment
Я не думаю, что ваша функция обрабатывает негативы. Мне пришлось изменить его на doubleval(preg_replace("/([^0-9\-\\.])/i", "", $stringVal)). Мой выглядит нормально или что-то не так с моим подходом? - person Ryan; 19.02.2019

Вам нужно удалить символ валюты из строки. PHP intval останавливается на первом найденном нечисловом символе.

$int = intval(preg_replace('/[^\d\.]/', '', '$100')); // 100

Хотя, если у вас есть значение, подобное $100.25, вместо этого вы можете использовать floatval.

$float = floatval(preg_replace('/[^\d\.]/', '', '$100.25')); // 100.25
person Rocket Hazmat    schedule 13.09.2011
comment
ИМХО это лучший ответ - person Timo Huovinen; 11.07.2013
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013

В PHP есть intval (вот документы), которые (насколько я могу судить) точно такие же, как в JavaScript parseInt.

Однако, что бы ни стоило, я не думаю, что какая-либо функция поможет вам в том, что вы пытаетесь сделать. Поскольку первый символ не числовой, оба сходят с ума (PHP даст вам 0, JS даст вам NaN). Итак, на любом языке вам придется выполнить синтаксический анализ строк/регулярных выражений.

person sdleihssirhc    schedule 13.09.2011
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013

Я новичок, поэтому, вероятно, у описанного ниже подхода есть очевидный (для других, а не для меня) недостаток, но я все равно решил поделиться им. Мне было бы интересно узнать, быстрее это или медленнее, чем использование preg_replace, но я не проводил тестирования скорости.

$badChars = array("$", ",", "(", ")");
$dirtyString = "($3,895.23)";
$cleanString = str_ireplace($badChars, "", $dirtyString);
echo "$dirtyString becomes $cleanString<p>";

$dirtyString может быть массивом, поэтому:

$badChars = array("$", ",", "(", ")");
$dirtyStrings = array("($3,895.23)", "1,067.04", "$5683.22", "$9834.48");
$cleanStrings = str_ireplace($badChars, "", $dirtyStrings);

echo var_dump($cleanStrings);
person user2840024    schedule 02.10.2013
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013
comment
Как и некоторые другие предложения, это ограничено локалями, которые используют . как десятичный и , как разделитель тысяч. Для тех, кто использует запятую в качестве десятичного разделителя, НЕ является плохим символом. - person lawnbowler; 27.04.2015

Кастинг — твой друг:

$int = (int) $string;

Обновление на основе операции:

Попробуйте что-то вроде этого:

<?php

function extract_numbers($string)
{
    return preg_replace("/[^0-9]/", '', $string);
}

echo extract_numbers('$100');

?>

Демонстрация: http://codepad.org/QyrfS7WE

person Naftali aka Neal    schedule 13.09.2011
comment
Будет ли это работать, когда строка содержит другие нечисловые символы, такие как примеры OP? - person Oliver Charlesworth; 14.09.2011
comment
@Rocket -- хммм, теперь я вижу это... смотрю на это - person Naftali aka Neal; 14.09.2011
comment
Я экспериментировал с этим, прежде чем опубликовать свой вопрос, но это не сработало - я получаю тот же результат, что и @Rocket. - person Bojangles; 14.09.2011
comment
Спасибо, Нил. Ваш ответ такой же, как у @rlemon, так что оба верны. Я отмечаю ответ rlemon как правильный, поскольку у него меньше представителей, чем у вас, но тем не менее я благодарю вас за ваши усилия. - person Bojangles; 14.09.2011
comment
Возможно, вы захотите добавить . в регулярное выражение, иначе $100.00 будет равно 10000. - person Rocket Hazmat; 14.09.2011
comment
@JamWaffles - на самом деле это не так, но ладно.... Особенно, поскольку мое новое обновление было еще до того, как этот пользователь опубликовал ответ, но что угодно... - person Naftali aka Neal; 14.09.2011
comment
Я приму любой ответ, который я считаю а) правильным и б) заслуживающим репутации больше всего. Поскольку оба ваших ответа очень похожи, rlemon получает галочку на основе второго критерия. - person Bojangles; 14.09.2011
comment
ооо... это очень странный и непоследовательный способ выбора ответа. - person Naftali aka Neal; 14.09.2011
comment
По моему собственному мнению, я так не думаю, но я просто оставлю это здесь. - person Bojangles; 14.09.2011
comment
просто как примечание к исходному ответу. после публикации моего нового обновления может показаться, что кастинг не мой друг. :) просто надо было это сказать - person rlemon; 14.09.2011
comment
FYI: объединено здесь из stackoverflow.com/questions /7407946/ - person Shog9; 18.11.2013

У меня была аналогичная проблема, когда я не получил символ валюты, а только строки (например: 1 234 567,89 или 1 234 567,89).

Это помогло мне нормализовать оба случая в числа с плавающей запятой:

$val = str_replace(",", ".", $formatted);
$val = preg_replace("/[\,\.](\d{3})/", "$1", $val);

Но ответ Гордона намного чище.

person Edson Medina    schedule 27.03.2012
comment
Я оставил здесь что-то более конкретное: stackoverflow.com/a/13112263/367456 - однако он не обрабатывает валюту символы вокруг самого номера. - person hakre; 28.10.2012
comment
затем просто добавьте это, чтобы отфильтровать все нецифровые символы и символы, не являющиеся разделителями: preg_replace ('/[^\d\,\.]/', '', $val); - person Edson Medina; 29.10.2012