Asked Nov 16th, 2020 10:02 a.m. 193 0 3
  • 193 0 3
+1

Toán tử php

Share
  • 193 0 3

Em có bài toán dạng như thế này

  • Người dùng sẽ nhập các công thức dạng a*b+c+d-e hoặc a*b*c-d-e ,... và các công thức này sẽ được lưu vào database
  • Khi các biến a,b,c,d,e kia có giá trị em cần gán vào một trong các công thức đã lưu trên database để ra kết quả thì có cách xử lý nào bằng php không ạ.
Avatar Trần Xuân Thắng @tranxuanthang
Nov 16th, 2020 10:05 a.m.

ab*c-d-e thế ý bạn là ab kia có phải nhân vào nhau không?

0
| Reply
Share
Avatar Thanh Hai @thanh_hai
Nov 16th, 2020 10:08 a.m.

@tranxuanthang em vừa sửa lại rồi trình soạn thảo gõ a * b thành ab luôn 😦

0
| Reply
Share
Avatar Trần Xuân Thắng @tranxuanthang
Nov 16th, 2020 10:11 a.m.

@thanh_hai Chỉ có cộng , trừ nhân chia, với các số, chữ a, b, c... thôi hả bạn

0
| Reply
Share
Avatar Thanh Hai @thanh_hai
Nov 16th, 2020 10:16 a.m.

@tranxuanthang đúng rồi bác chỉ là cộng , trừ nhân chia nhưng có thể có cả trong ngoặc nữa vd: a * (b+c)-e/d

+1
| Reply
Share
Avatar Ngoc N Tran @ngoctnq
Nov 16th, 2020 10:25 a.m.
eval

(làm ơn đừng dùng, đây là một câu trả lời đùa.)

0
| Reply
Share

3 ANSWERS


Answered Nov 16th, 2020 11:00 a.m.
+3

Cách đúng và an toàn nhất theo mình:

  • Chuyển biểu thức kia từ dạng infix hiện tại (rất kém thân thiện với máy tính) thành một dạng khác dễ phân tích đối với máy tính hơn như postfix hay AST. Có thể thực hiện chuyển đổi ngay khi lưu vào database.
  • Thực hiện tính toán phép toán ở dạng postfix. Trong quá trình tính toán, thay thế giá trị tương ứng vào các biến a, b, c,... Lúc này việc tính toán khá đơn giản rồi, như với postfix thì có thể dễ dàng dùng stack để tính.

Các thuật toán chuyển từ infix sang postfix hay thực hiện tính giá trị biểu thức ở dạng postfix đều không quá khó và có rất nhiều trên mạng. Và với php mình cũng tin là có nhiều library cung cấp khả năng parse các phép toán đơn giản thế này. Cái bạn cần tránh dùng ở đây là sử dụng eval() của php, nó tiềm ẩn nhiều nguy cơ bảo mật nếu bạn không xác thực đầu vào cẩn thận.

Share
Answered Nov 16th, 2020 10:49 a.m.
+1

Đầu tiên bạn thêm class này:

class Field_calculate {
    const PATTERN = '/(?:\-?\d+(?:\.?\d+)?[\+\-\*\/])+\-?\d+(?:\.?\d+)?/';

    const PARENTHESIS_DEPTH = 10;

    public function calculate($input){
        if(strpos($input, '+') != null || strpos($input, '-') != null || strpos($input, '/') != null || strpos($input, '*') != null){
            //  Remove white spaces and invalid math chars
            $input = str_replace(',', '.', $input);
            $input = preg_replace('[^0-9\.\+\-\*\/\(\)]', '', $input);

            //  Calculate each of the parenthesis from the top
            $i = 0;
            while(strpos($input, '(') || strpos($input, ')')){
                $input = preg_replace_callback('/\(([^\(\)]+)\)/', 'self::callback', $input);

                $i++;
                if($i > self::PARENTHESIS_DEPTH){
                    break;
                }
            }

            //  Calculate the result
            if(preg_match(self::PATTERN, $input, $match)){
                return $this->compute($match[0]);
            }
            // To handle the special case of expressions surrounded by global parenthesis like "(1+1)"
            if(is_numeric($input)){
                return $input;
            }

            return 0;
        }

        return $input;
    }

    private function compute($input){
        $compute = create_function('', 'return '.$input.';');

        return 0 + $compute();
    }

    private function callback($input){
        if(is_numeric($input[1])){
            return $input[1];
        }
        elseif(preg_match(self::PATTERN, $input[1], $match)){
            return $this->compute($match[0]);
        }

        return 0;
    }
}

Sau đó, giả sử bạn có công thức lấy từ DB và gán vào biến như sau :

$f1 = "(a*b+c+d-e)/3";

và các biến giá trị là

$a = 2;
$b = 3;
$c = 5;
$d = 2;
$e = 9;

thì bạn sẽ xử lý như sau:

$f1 = str_replace("a", $a, $f1);
$f1 = str_replace("b", $b, $f1);
$f1 = str_replace("c", $c, $f1);
$f1 = str_replace("d", $d, $f1);
$f1 = str_replace("e", $e, $f1);

$Cal = new Field_calculate();
$result = $Cal->calculate($f1);
echo $result;
Share
Answered Nov 19th, 2020 2:28 a.m.
0

bạn tìm hiểu "Ký pháp Ba Lan/ Ba Lan đảo" nhé, Chuyển công thức thường về ký pháp Ba Lan Đảo rồi thay giá trị để tính ra kết quả.

Share
Viblo
Let's register a Viblo Account to get more interesting posts.