在PHP中接受带小数和千位分隔符的国际数字 [英] Accepting international numbers with decimal and thousands separator in PHP

查看:254
本文介绍了在PHP中接受带小数和千位分隔符的国际数字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For an online calculator where users may enter an energy amount to calculate corresponding fees, I need the PHP script to accept various user inputs. The value of "2 million and one fourth joule" may be entered as:

2000000.25(默认表示法)

2000000.25 (default notation)

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

2,000,000.25 (with thousands separator)

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

2000000,25 (comma as decimal point)

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

2.000.000,25 (comma as decimal point, with thousands separator)

2'000'000.25(其他格式)

2'000'000.25 (alternative format)

2 000,25(法语表示法)

2 000 000,25 (French notation)

我如何使脚本意识到这种差异?

How could I make the script aware of such differences?

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

My first try was to just str_replace alternative characters with the default ones, but the period (.) may be either a decimal or a thousands separator. I tried using sscanf but how can I make sure that it reads the number correctly?

大多数用户只会在小数点后提供两位数字,但是我有什么办法可以区分1.234(1点234,句点作为小数点分隔符)和1.234(1,234.24,句点作为千位分隔符) ?

Most users will only provide two digits after the decimal point, but is there any way I can distinguish 1.234 (1 point 234, period as decimal separator) and 1.234 (one thousand two hundred thirty-four, period as thousands separator)?

推荐答案

由于我无法通过一些内置的PHP函数找到简单的解决方案,因此我编写了两个函数来(1)检查输入的字符串是否可以完全是一个数字,并且(2)是否根据所使用的分隔符格式正确.

Since I wasn't able to find a simple solution via some built-in PHP functions, I wrote two functions to (1) check if the entered string may be a number at all and (2) if it is well-formed depending on the separators used.

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

I restricted the possible separators to period (.), comma (,), space () and apostrophe (') as thousands separators. The decimal point may only be one of the first two options. Both sets of separators can be edited to allow even more or restrict the ones in place.

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

What I am actually doing is to look for all number columns and all separators by using a couple of simple preg_match_all calls.

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

The complete code reads as follows and should be self-explaining as I added some comments when throwing a false. I'm sure, this can be simplified somehow, but it works right now and filters many errors while allowing even some strange combinations such as 2 000 000.25 or 2'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位数字.

I am aware of one flaw this set of function has: 1.234 or 1,234 will always be treated as the whole number 1234, as the function assumes the separator must be a thousands separator if there are less than 4 digits in front of the single separator.

这篇关于在PHP中接受带小数和千位分隔符的国际数字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆