4

ユーザーがエネルギー量を入力して対応する料金を計算できるオンライン計算機の場合、さまざまなユーザー入力を受け入れる PHP スクリプトが必要です。「200 万と 4 分の 1 ジュール」の値は、次のように入力できます。

2000000.25(デフォルト表記)

2,000,000.25 (桁区切りあり)

2000000,25 (小数点はカンマ)

2.000.000,25 (小数点はコンマ、桁区切り記号付き)

2'000'000.25 (代替形式)

2 000 000,25 (フランス語表記)

このような違いをスクリプトに認識させるにはどうすればよいでしょうか?

私の最初の試みは、代わりの文字をデフォルトのものに str_replace することだけでしたが、ピリオド (.) は小数点または桁区切りのいずれかである可能性があります。sscanf を使用してみましたが、番号が正しく読み取られるようにするにはどうすればよいですか?

ほとんどのユーザーは小数点以下 2 桁しか提供しませんが、1.234 (1 ポイント 234、小数点としてのピリオド) と 1.234 (1,234、千単位の区切りとしてのピリオド) を区別できる方法はありますか?

4

2 に答える 2

3

意味を知る方法はありません1.234

  • 小数点記号を決める
  • 最後の,または.そのセパレーターに置き換えます
  • セパレータを 1 つだけ許可する
  • 入力を数値に制限し,.
于 2012-11-22T12:46:37.963 に答える
2

いくつかの組み込み PHP 関数を使用して簡単な解決策を見つけることができなかったので、(1) 入力された文字列が数値であるかどうかを確認するための 2 つの関数を作成しました。使用されるセパレーター。

千単位の区切り記号として、可能な区切り文字をピリオド ( .)、コンマ ( ,)、スペース ( )、およびアポストロフィ ( )に制限しました。'小数点は、最初の 2 つのオプションの 1 つだけです。セパレーターの両方のセットを編集して、さらに多くのセパレーターを許可したり、所定の位置にあるものを制限したりできます。

私が実際に行っているのは、いくつかの単純なpreg_match_all呼び出しを使用して、すべての数値列とすべての区切り記号を探すことです。

完全なコードは次のようになりますfalse。確かに、これは何らかの形で単純化できると思いますが、現在は機能しており、多くのエラーをフィルタリングしながら、2 000 000.25またはなどの奇妙な組み合わせも許可してい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 つの欠陥を認識しています: 1.234or1,234は常に整数として扱われます1234。関数は、1 つの区切り記号の前に 4 桁未満の桁がある場合、区切り記号が千単位の区切り記号である必要があると想定しているためです。

于 2012-11-22T16:40:25.867 に答える