转自http://blog.youkuaiyun.com/laxsong/article/details/6873024 ,优化了负号的支持。
原文中,对负号的支持不是太好,并且使用
- Double.toString(Double.parseDouble(d1)
- / Double.parseDouble(d2));
- 在一些数据下运算可能会出现越界的情况。
- 在此 也感谢 laxsong 的分享 。
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Stack;
/**
* Created by Heavenick on 2015/12/10.
*/
public class WinsonEval {
private final static String ERR_NOT_END_VALID = "The last character of your expression MUST be '#'!";
private final static String ERR_PARENTHESE_NOT_PAIR = "'(' & ')' should be used in pair!";
private final static String ERR_CHAR_NOT_SUPPORT = "Not supported character";
private final static String ERR_OPERATION_NOT_SUPPORTED = "Not supported operation";
private final static String ERR_OPERATOR_NOT_VALID = " doesn't support double data!";
private final static String ERR_UNKNOWN = "An unknown error!";
private static boolean flag_double;
/*
* expression must be end with #
*/
public static String eval(String expression) {
ArrayList<String> list;
try {
list = toSuffixSequence(expression);
System.out.println(list);
return calculate(list);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static boolean isResultDouble() {
return flag_double;
}
private static String calculate(ArrayList<String> list) throws Exception {
Stack<String> stack = new Stack<String>();
for (String s : list) {
if (isOperator(s)) {
String d1 = stack.pop();
String d2 = stack.pop();
String res = doCalc(d2, d1, s);
stack.push(res);
} else
stack.push(s);
}
if (stack.size() == 1)
return stack.pop();
else
throw new Exception(ERR_UNKNOWN);
}
private static String doCalc(String d1, String d2, String oper) throws Exception {
if (oper != null && oper.length() > 1)
throw new Exception(ERR_OPERATION_NOT_SUPPORTED + ":'" + oper + "'");
switch (oper.charAt(0)) {
case '+':
return new BigDecimal(d1).add(new BigDecimal(d2)).toPlainString();
case '-':
return new BigDecimal(d1).subtract(new BigDecimal(d2)).toPlainString();
case '*':
return new BigDecimal(d1).multiply(new BigDecimal(d2)).toPlainString();
case '/':
return new DecimalFormat("####.#").format(new BigDecimal(d1).divide(new BigDecimal(d2),4,BigDecimal.ROUND_HALF_UP).doubleValue());
case '%':
return new BigDecimal(d1).divideAndRemainder(new BigDecimal(d2))[0].toPlainString();
default:
throw new Exception(ERR_OPERATION_NOT_SUPPORTED + ":'" + oper + "'");
}
}
private static boolean isDoubleNeeded(String d1, String d2, String oper) {
if (d1.contains(".") || d2.contains("."))
return true;
if (oper != null && oper.equals("/")) {
int left = Integer.parseInt(d1) % Integer.parseInt(d2);
if (left != 0)
return true;
}
return false;
}
private static boolean isOperator(String str) {
if (str != null
&& (str.equals("+") || str.equals("-") || str.equals("*")
|| str.equals("/") || str.equals("%")))
return true;
return false;
}
private static ArrayList<String> toSuffixSequence(String expression)
throws Exception {
if (!expression.endsWith("#"))
throw new Exception(ERR_NOT_END_VALID);
ArrayList<String> list = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
stack.push("#");
char last, ch;
StringBuffer sb = null;
for (int i = 0; i < expression.length(); i++) {
ch = expression.charAt(i);
switch (ch) {
case '+':
case '-':
case '*':
case '/':
case '%':
last = stack.peek().charAt(0);
//TODO 如果当前字符不是“-” 并且前一个不是运算符,或者堆里面的数据数大于等于2个,才将 运算符压入堆中
if( ! ( ch == '-' && isOperator(last+"") ) || list.size() >=2 ) {
if (last != '(' && priority(last) >= priority(ch))
list.add(stack.pop());
}
stack.push("" + ch);
break;
case '(':
stack.push("(");
break;
case ')':
while (!stack.isEmpty() && stack.peek().charAt(0) != '(')
list.add(stack.pop());
if (stack.isEmpty() || stack.size() == 1)
throw new Exception(ERR_PARENTHESE_NOT_PAIR);
stack.pop();
break;
case '#':
while (stack.size() > 1 && stack.peek().charAt(0) != '(')
list.add(stack.pop());
if (stack.size() > 1)
throw new Exception(ERR_PARENTHESE_NOT_PAIR);
break;
default:
if (Character.isDigit(ch) || '.' == ch) {
sb = new StringBuffer();
sb.append(ch);
while (Character.isDigit(expression.charAt(i + 1)) || expression.charAt(i + 1) == '.')
sb.append(expression.charAt(++i));
String ls = stack.pop();
String n = sb.toString();
// 如果前一个字符是“-” 将进行判断,是不是负号
if("-".equals(ls)){
if( ("#".equals(stack.peek()) && list.size() == 0) || (isOperator(stack.peek()) )) {
n = "-" + n;
}else{
stack.push("-");
}
}else{
stack.push(ls);
}
list.add(n);
break;
} else
throw new Exception(ERR_CHAR_NOT_SUPPORT + ":'" + ch + "'");
}
}
return list;
}
private static int priority(char ch) {
switch (ch) {
case '+':
case '-':
return 1;
case '*':
case '/':
case '%':
return 2;
case '#':
return 0;
default:
return 0;
}
}
public static void main(String[] args) {
String expression = "2+-7#";
try {
System.out.println(WinsonEval.eval("(2*2+6)/5+3#").toString());
System.out.println(WinsonEval.eval("2+-7#"));
System.out.println(WinsonEval.eval("2-7#"));
System.out.println(WinsonEval.eval("-2+7#"));
System.out.println(WinsonEval.eval("3/6#"));
System.out.println(WinsonEval.eval("3/2#"));
System.out.println(WinsonEval.eval("2/3#"));
System.out.println(WinsonEval.eval("12*15.12-12#"));
System.out.println(WinsonEval.eval("(2*-2+6)/5+3#"));
// && !StringUtils.containsAny(stack.peek(),new char[]{'*','/'})
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println(Character.getType('.'));
// System.out.println(Character.getType('1'));
}