+1
Toán tử php
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 ạ.
3 CÂU TRẢ LỜI
+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.
+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;
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ả.
ab*c-d-e
thế ý bạn là ab kia có phải nhân vào nhau không?@tranxuanthang em vừa sửa lại rồi trình soạn thảo gõ a * b thành ab luôn
@thanh_hai Chỉ có cộng , trừ nhân chia, với các số, chữ a, b, c... thôi hả bạn
@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
(làm ơn đừng dùng, đây là một câu trả lời đùa.)