解析数字并保留负数

5
我正在尝试将数字恢复为其原始格式,但保留其正负性。Stack Overflow上的某个用户向我推荐了以下代码,它可以很好地工作,但不保留负数。
请问是否有人能够帮我找到更好的解决方法?
编辑 - 适用于美元货币/普通数字
示例:
1,234 = 1234 -1,234 = -1234 1,234.00 = 1234 1,234.56 = 1234.56
function numberUnformat($number)
{
   $cleanString = preg_replace('/([^0-9\.,])/i', '', $number);
   $onlyNumbersString = preg_replace('/([^0-9])/i', '', $number);

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

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

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

1
这需要支持国际化格式的数字吗(例如 1.000,01)? - Mike Brant
不,它不会。好问题。 - bryan
“unformat” 最好拼写为 “parse”。 - Eric
@bryan:你发布的那段代码试图处理国际化。 - Eric
希望你能根据下面的一些答案注意到,这个问题并不是最适合使用正则表达式解决的。 - Mike Brant
6个回答

6
如果您有ICU扩展(在PHP 5.3中捆绑)可用,请尝试以下操作:
$formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
echo $formatter->parse('-1,234.56');

1
这个扩展的使用非常好。 - Mike Brant
这个解决方案比使用正则表达式清晰得多。 - Jud

2

将你的正则表达式修改为匹配负数:

$cleanString = preg_replace('/([^\-0-9\.,])/i', '', $number);

测试用例:

echo numberUnformat('1,234')."\n";
echo numberUnformat('-1,234')."\n";
echo numberUnformat('1,234.00')."\n";
echo numberUnformat('1,234.56 ')."\n";

输出:

1234
-1234
1234
1234.56

示例!


这个工作非常顺利,除了我在MySQL中有一个DECIMAL(10,2),它保留了末尾的.00。有没有办法解析它? - bryan
@bryan:抱歉,我不明白?你能否给我一个字符串的例子,说明这个正则表达式无法替换? - anon

2
如果您想在字符串中删除任何多余的减号,也可以这样做:
$cleanString = preg_replace('/[^0-9.,-]|(?<=.)-/', '', $number);
$onlyNumbersString = preg_replace('/[^0-9-]|(?<=.)-/', '', $number);

注意,在原始文本中,您不需要使用括号、反斜杠或/i标记。

你为什么要使用 (?<=.)?这只会防止字符串开头的 - 被替换。 - HamZa
2
@HamZa - 没错。我们希望保留字符串开头的“-”,并在其他地方删除它们。 - Mark Reed

2
我会给函数添加一些参数来允许指定分组和小数符号(还可以允许将其转换为float或decimal,并采用以下解决方案:
function number_unformat($num_string, $group_sep = ',', $dec_sep = '.', $cast_to_type = true) {
    if (substr_count($num_string, $dec_sep) > 1) {
        // input was invalid
        throw new Exception('Inavlid string: `' . $num_string . '` passed to function. Too many decimal separators.');
    }   
    // remove grouping separator
    $string = str_replace($group_sep, '', $num_string);
    if (true === $cast_to_type) {
        // change any decimal separators to periods before casting
        $string = str_replace($dec_sep, '.', $string, $count);
        if ($count === 1) {
            return (float)$string;
        } else {
            return (int)$string;
        }
    } else {
        return $string;
    }
}

请注意,这里完全不需要使用正则表达式。

@bryan 谢谢,不过说实话,如果你要涉足国际化领域,你可能想使用 Andreas Baumgart 提供的建议。 - Mike Brant
并不是真正深入研究国际化,只是为了防止打错字而已。 - bryan
由于某些原因,此代码在没有小数的 $_POST 变量上无法正常工作。我不知道为什么。 - bryan
@bryan,你能展示一下你当前的使用情况以及可能有的调试结果吗? - Mike Brant

0
一个相当快速(虽然不完美)的修复方法是更改函数的前两行:
$cleanString = preg_replace('/([^-0-9\.,])/i', '', $number);
$onlyNumbersString = preg_replace('/([^-0-9])/i', '', $number);

如果你有一个像2-434.43这样的数字,这将会导致问题。


0

可以通过修改正则表达式来保留负数,但对我来说,在最后执行以下操作更简单:

$absvalue = (float) str_replace(',', '.', $removedThousendSeparator);
if ($number[0] == '-') {
    $absvalue = $absvalue * -1.0;
}
return $absvalue;

我可能在那里有语法错误,我的 PHP 有点生疏,但是想法只是检查并查看输入字符串是否以负号开头,如果是,将结果乘以负1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接