最近在考虑一个问题:公司项目可能会使用运算模板来计算相应的值,模板freemarker、velocity都是不错的选择。那通过模板将计算公式字符串组装出来后,就需要解析字符串得到计算结果,以下是我的实现:
public class Arithmetic {
private char[] oper = { '+', '-', '*', '/', '(', ')' };
public static void main(String args[]) {
Arithmetic arith = new Arithmetic();
System.out.println(System.currentTimeMillis());
BigDecimal result = arith.parserExpression("(1+2/(3+(1+2.01)))*3/(1+2*(3+(1+2.01)))*3/(1+2*(3+(1+2.01)))*3/(1+2*(3+(1+2.01)))*3");
System.out.println(System.currentTimeMillis());
System.out.println(result.toString());
}
private boolean contains(char[] array, char v) {
for (char e : array)
if (e == v)
return true;
return false;
}
public BigDecimal parserExpression(String expression) {
Stack<Object> stack1 = new Stack<Object>();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (this.contains(oper, c)) {
if (sb.length() > 0) {
BigDecimal bigdecimal = new BigDecimal(sb.toString());
stack1.push(bigdecimal);
sb = new StringBuffer();
}
stack1.push(c);
} else {
sb.append(c);
if (i == expression.length() - 1) {
BigDecimal bigdecimal = new BigDecimal(sb.toString());
stack1.push(bigdecimal);
}
}
}
Stack<Object> stack2 = new Stack<Object>();
while (!stack1.isEmpty()) {
Object o = stack1.pop();
if (o instanceof Character) {
char c = (Character) o;
if (c == '(') {
//处理最小运算单元
BigDecimal subResult = this.subExpression(stack2);
stack2.push(subResult);
continue;
}
}
stack2.push(o);
}
return this.subExpression(stack2);
}
private BigDecimal subExpression(Stack<Object> stack) {
ArrayList<Object> list = new ArrayList<Object>();
while (!stack.isEmpty()) {
Object o = stack.pop();
if (o instanceof Character) {
char c = (Character) o;
if (c == ')') {
break;
}
}
list.add(o);
}
return calculation(list);
}
private BigDecimal calculation(ArrayList<Object> list) {
Stack<Object> stack3 = new Stack<Object>();
boolean priority = false;
char sign = 0;
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
if (o instanceof Character) {
char c = (Character) o;
if (c == '*' || c == '/') {
priority = true;
sign = c;
continue;
}
}
if (priority) {
Object preObject = stack3.pop();
BigDecimal preBigDecimal = (BigDecimal) preObject;
BigDecimal nxtBigDecimal = (BigDecimal) o;
if (sign == '*') {
BigDecimal result = preBigDecimal.multiply(nxtBigDecimal);
stack3.push(result);
} else {
BigDecimal result = preBigDecimal.divide(nxtBigDecimal, 6, BigDecimal.ROUND_HALF_UP);
stack3.push(result);
}
priority = false;
sign = 0;
continue;
}
stack3.push(o);
}
BigDecimal result = new BigDecimal(0);
while (!stack3.isEmpty()) {
Object last = stack3.pop();
if (last instanceof Character) {
throw new RuntimeException();
}
if (stack3.isEmpty()) {
result = result.add((BigDecimal) last);
return result;
}
Object abs = stack3.pop();
if (!(abs instanceof Character)) {
throw new RuntimeException();
}
char c = (Character) abs;
if (c == '+') {
result = result.add((BigDecimal) last);
} else {
result = result.add(((BigDecimal) last).negate());
}
}
return result;
}
}