java实现数学表达式计算(采用后缀表达式)

业务中需要根据业务模板自动计算业务数据,采用jsexpression计算性能很差,因此需要自己实现。

直接上代码

package com.longersoftware.lfcp.modules.basic.utils;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.Stack;

import com.longersoftware.logging.LogbackLogger;
import com.longersoftware.logging.LogbackLoggerFactory;

public class ExpressionUtils {
	private static final LogbackLogger logger = LogbackLoggerFactory.getLogger(ExpressionUtils.class);
	private static final char NEGATIVE = '~';

	private Stack cStack = new Stack();
	private Stack iStack = new Stack();

	public String eval(String expression) {
		String ret = "NA";
		try {
			double re = this.calc(expression.toCharArray());
			BigDecimal b = new BigDecimal(re);
			re = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
			ret = re + "";
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("expression error.{}", expression);
		}
		return ret;
	}

	// 符号等级
	static int cLevel(char c) {
		switch (c) {
		case '(':
			return 0;
		case '+':
			return 2;
		case '-':
			return 2;
		case '*':
		case '/':
			return 3;
		}
		return 0;
	}

	// 对栈进行运算
	private void dealStack() {
		char c = cStack.pop();
		double num1 = iStack.pop();
		double num2 = iStack.pop();
		switch (c) {
		case '+':
			iStack.push(num1 + num2);
			break;
		case '-':
			iStack.push(num2 - num1);
			break;
		case '*':
			iStack.push(num1 * num2);
			break;
		case '/':
			iStack.push(num2 / num1);
			break;
		}
	}

	// 返回str的表达式的值
	public double calc(char[] exp) {
		while (!cStack.isEmpty()) {
			cStack.pop();
		}
		while (!iStack.isEmpty()) {
			iStack.pop();
		}

		char tmp, pre = '^';
		boolean findPo = false;
		int l = 0;
		for (int i = 0; i < exp.length; i++) {
			tmp = exp[i];
			if (tmp == ' ') {
				continue;
			} else if (tmp >= '0' && tmp <= '9') {
				double num = tmp - '0';
				findPo = false;
				while (i < exp.length - 1 && (exp[i + 1] == '.' || exp[i + 1] >= '0' && exp[i + 1] <= '9')) {
					i++;
					tmp = exp[i];
					if (tmp == '.') {
						findPo = true;
						l = 0;
						continue;
					}
					if (findPo) {
						num = num + (tmp - '0') / (Math.pow(10, ++l));
					} else {
						num = num * 10 + tmp - '0';
					}
				}
				// 处理负数
				if (!cStack.isEmpty() && cStack.peek() == NEGATIVE) {
					cStack.pop();
					num = num * -1;
				}
				iStack.push(num);
			} else if (tmp == '(') {
				cStack.push(tmp);
			} else if (tmp == '+' || tmp == '-' || tmp == '*' || tmp == '/') {
				// 处理负数
				if ((tmp == '+' || tmp == '-') && ("+-*/(^").indexOf(pre) != -1) {
					if (tmp == '-') {
						cStack.push(NEGATIVE);
					}
					continue;
				}
				while (!cStack.isEmpty() && cLevel(tmp) <= cLevel(cStack.peek())) {
					dealStack();
				}
				cStack.push(tmp);
			} else if (tmp == ')') {
				while (!cStack.isEmpty() && !cStack.peek().equals('(')) {
					dealStack();
				}
				cStack.pop();
			}
			pre = tmp;
		}
		while (!cStack.isEmpty()) {
			dealStack();
		}
		return iStack.pop();
	}

	/**
	 * 校验表达式
	 * 
	 * @param value
	 * @return
	 */
	public static String parseExpression(String value) {
		String ret = "";
		Stack cStack = new Stack();
		if (!value.startsWith("=")) {
			ret = "!" + value;
			return ret;
		}
		for (char c : value.toCharArray()) {
			switch (c) {
			// case '=':
			// case '+':
			// case '-':
			// case '*':
			// case '/':
			case '(':
			case '{':
			case '[':
				cStack.push(c);
				break;
			case '}':
				if (!cStack.isEmpty() && cStack.peek() == '{') {
					cStack.pop();
				} else {
					cStack.push(c);
				}
				break;
			case ')':
				if (!cStack.isEmpty() && cStack.peek() == '(') {
					cStack.pop();
				} else {
					cStack.push(c);
				}
				break;
			case ']':
				if (!cStack.isEmpty() && cStack.peek() == '[') {
					cStack.pop();
				} else {
					cStack.push(c);
				}
				break;
			}
		}

		if (cStack.isEmpty()) {
			ret = value;
		} else {
			ret = "!" + value;
		}
		return ret;
	}

	public static boolean isExpression(Object value) {
		boolean ret = false;
		if (Objects.isNull(value)) {
			return ret;
		}
		if (Number.class.isAssignableFrom(value.getClass()) || value instanceof Boolean) {
			return ret;
		}
		if (value.toString().startsWith("=")) {
			ret = true;
		}
		return ret;
	}

	public static void main(String a[]) {
		ExpressionUtils utils = new ExpressionUtils();
		System.out.println("-1 + 2-3+-1=" + utils.calc("-1 + 2-3+-1".toCharArray()));
		System.out.println("-1 + 2-(3+-1)=" + utils.calc("-1 + 2-(3+-1)".toCharArray()));
		System.out.println("-1=" + utils.calc("-1".toCharArray()));
		System.out.println("2.0=" + utils.calc("2.0".toCharArray()));
		System.out.println("29.0=" + utils.calc("29.0".toCharArray()));
		System.out.println("-1*+4 + 2-3+-1=" + utils.calc("-1*+4 + 2-3+-1".toCharArray()));
		System.out.println("-1*+4 + 2-(3+-1)=" + utils.calc("-1*+4 + 2-(3+-1)".toCharArray()));
		System.out.println("100-1-4=" + utils.calc("100-1-4".toCharArray()));
	}
}

上述代码支持简单的数学计算,不支持单目运算符。

在进行减法计算时,遇到个坑。

由于采用后缀表达式,在运算符入栈时,优先级搞得弹出计算,但级别相等的未弹出,出现减法错误,例如:

5-3-2,正确值为0,实际得到4,这是因为减法等在先计算式,需要变号;解决办法为,在比较运算符优先级时,相同级别的同样弹出计算。

while (!cStack.isEmpty() && cLevel(tmp) <= cLevel(cStack.peek())) {
	dealStack();
}

 

你可能感兴趣的:(java)