【问题标题】:Accepting international numbers with decimal and thousands separator in PHP在 PHP 中接受带小数和千位分隔符的国际数字
【发布时间】:2012-11-10 20:46:21
【问题描述】:

对于一个用户可以输入能量数量来计算相应费用的在线计算器,我需要 PHP 脚本来接受各种用户输入。 “200万又四分之一焦耳”的值可以输入为:

2000000.25(默认表示法)

2,000,000.25(带千位分隔符)

2000000,25(逗号作为小数点)

2.000.000,25(逗号作为小数点,用千位分隔符)

2'000'000.25(替代格式)

2 000 000,25(法国符号)

如何让脚本意识到这些差异?

我的第一次尝试是用默认字符替换替代字符,但句点 (.) 可以是小数或千位分隔符。我尝试使用 sscanf,但如何确保它正确读取数字?

大多数用户只会提供小数点后两位,但是有什么方法可以区分 1.234(1 点 234,句点作为小数分隔符)和 1.234(一千二百三十四,句点作为千位分隔符) ?

【问题讨论】:

  • 我想您可以通过查看 Accept: 标头做出有根据的猜测,但您确实应该让您的用户标准化某些东西……如果必须,将控件分成两部分。跨度>

标签: php number-formatting setlocale


【解决方案1】:

没有办法知道1.234 是什么意思。

  • 决定小数点分隔符
  • 将最后一个 ,. 替换为该分隔符
  • 只允许一个分隔符
  • 将输入限制为数值,,.

【讨论】:

  • 我已经这么认为了。其余的任何解决方案(省略那个模棱两可的部分)?由于它是一个供来自不同国家/地区的用户使用的工具,我真的不想限制输入。
  • 这是一个真正的问题吗?我个人不在输入字段中使用分隔符。无论如何,您不必阻止无效字符的输入,您可以静默删除它们。
  • 确实是个问题。该计算器适用于私人和小公司计算费用,具体取决于他们产生的能源量。虽然私人可能只输入低于 1000 kW 的值,但公司可以轻松达到几千甚至几百万千瓦。最令人担忧的问题是将英国私人(例如 15.75 kW)与德国公司(例如 15.750 kW,即 15750 kW 与千位分隔符的德国符号)分开。我现在正在研究使用两个 preg_match_all 表达式的解决方案。
  • 祝你好运,一定要进行广泛的测试:)
【解决方案2】:

由于我无法通过一些内置 PHP 函数找到简单的解决方案,因此我编写了两个函数来 (1) 检查输入的字符串是否可能是数字,以及 (2) 是否正确 -根据使用的分隔符形成。

我将可能的分隔符限制为句点 (.)、逗号 (,)、空格 () 和撇号 (') 作为千位分隔符。小数点可能只是前两个选项之一。两组分隔符都可以编辑以允许更多或限制现有的分隔符。

我实际上正在做的是通过使用几个简单的preg_match_all 调用来查找所有数字列和所有分隔符。

完整的代码如下所示,应该是不言自明的,因为我在抛出 false 时添加了一些 cmets。我敢肯定,这可以通过某种方式简化,但它现在可以工作并过滤许多错误,同时允许一些奇怪的组合,例如 2 000 000.252'000'000,25

    function check_number($number) {
        if ((int) substr($number,0,1) == 0) {
            return false; // not starting with a digit greater than 0
        }
        if ((string) substr($number,-1) != "0" && (int) substr($number,-1) == 0) {
            return false; // not ending with a digit
        }
        preg_match_all('/([^0-9]{2,})/', $number, $sep, PREG_PATTERN_ORDER);
        if (isset($sep[0][0])) {
            return false; // more than one consecutive non-digit character
        }
        preg_match_all('/([^0-9]{1})/', $number, $sep, PREG_PATTERN_ORDER);
        if (count($sep[0]) > 2 && count(array_unique($sep[0])) > 2) {
            return false; // more than 2 different separators
        }
        elseif (count($sep[0]) > 2) {
            $last_sep = array_pop($sep[0]);
            if (!in_array($last_sep,array(".",","))) {
                return false; // separator not allowed as last one
            }
            $sep_unique = array_unique($sep[0]);
            if (count($sep_unique) > 1) {
                return false; // not all separators (except last one) are identical 
            }
            elseif (!in_array($sep_unique[0],array("'",".",","," "))) {
                return false; // separator not allowed
            }
        }
        return true;
    }

    function convert_number($number) {
        preg_match_all('/([0-9]+)/', $number, $num, PREG_PATTERN_ORDER);
        preg_match_all('/([^0-9]{1})/', $number, $sep, PREG_PATTERN_ORDER);
        if (count($sep[0]) == 0) {
            // no separator, integer
            return (int) $num[0][0];
        }
        elseif (count($sep[0]) == 1) {
            // one separator, look for last number column
            if (strlen($num[0][1]) == 3) {
                if (strlen($num[0][0]) <= 3) {
                    // treat as thousands seperator
                    return (int) ($num[0][0] * 1000 + $num[0][1]);
                }
                elseif (strlen($num[0][0]) > 3) {
                    // must be decimal point
                    return (float) ($num[0][0] + $num[0][1] / 1000);
                }
            }
            else {
                // must be decimal point
                return (float) ($num[0][0] + $num[0][1] / pow(10,strlen($num[0][1])));
            }
        }
        else {
            // multiple separators, check first an last
            if ($sep[0][0] == end($sep[0])) {
                // same character, only thousands separators, check well-formed nums
                $value = 0;
                foreach($num[0] AS $p => $n) {
                    if ($p == 0 && strlen($n) > 3) {
                        return -1; // malformed number, incorrect thousands grouping
                    }
                    elseif ($p > 0 && strlen($n) != 3) {
                        return -1; // malformed number, incorrect thousands grouping
                    }
                    $value += $n * pow(10, 3 * (count($num[0]) - 1 - $p));
                }
                return (int) $value;
            }
            else {
                // mixed characters, thousands separators and decimal point
                $decimal_part = array_pop($num[0]);
                $value = 0;
                foreach($num[0] AS $p => $n) {
                    if ($p == 0 && strlen($n) > 3) {
                        return -1; // malformed number, incorrect thousands grouping
                    }
                    elseif ($p > 0 && strlen($n) != 3) {
                        return -1; // malformed number, incorrect thousands grouping
                    }
                    $value += $n * pow(10, 3 * (count($num[0]) - 1 - $p));
                }
                return (float) ($value + $decimal_part / pow(10,strlen($decimal_part)));
            }
        }
    }

我知道这组函数有一个缺陷:1.2341,234 将始终被视为整数 1234,因为该函数假定分隔符必须是千位分隔符,如果少于单个分隔符前 4 位数字。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 2018-05-11
    • 2014-11-01
    • 2013-11-07
    • 2013-04-15
    • 1970-01-01
    相关资源
    最近更新 更多