废话不多说直接上代码
public class Test {
public static void main(String[] args) {
System.out.println(exec("(((-2+4)+3)*((-1-5.3)*-2)-5)*3.1415926"));
}
/**
*
* @param inputs
* 带括号的四则表达式
* @return 运算结果
*/
public static BigDecimal exec(String inputs) {
int leftIndex = inputs.lastIndexOf('('); // 16
if (leftIndex == -1) {
// 字符串中没有括号 直接调用calc
return calculator(inputs);
} else {
// 如果有括弧先找最里面的(位置 再找对应的)位置,调用exec
int rightIndex = inputs.substring(leftIndex).indexOf(')') + leftIndex;
// 去独立的表达式,运算 calc(-1-5)
BigDecimal res = calculator(inputs.substring(leftIndex + 1, rightIndex));
// 重新组织表达式
inputs = inputs.substring(0, leftIndex) + res
+ inputs.substring(rightIndex + 1);
return exec(inputs);
}
}
/**
*
* @param exp
* 不带括号的四则表达式
* @return 运算结果
*/
public static BigDecimal calculator(String exp) {
// 1 . 获取所有四则运算的数字
List<BigDecimal> numbers = sliptNumbers(exp);
// 2. 获取所有四则运算的操作符号
List<Character> ops = sliptOperator(exp);
// 3. 先乘车运算
// 遍历运算符中的*和/
for (int i = 0; i < ops.size(); i++) {
// * /
// 获取运算符(不移除)
char op = ops.get(i);
// 如果是 * 或者 /, 从运算符的容器中移除,同是从数字容器中到对应该运算符位置的两个数字(移除数据,后面所有数据往前顺序移动)
if (op == '*' || op == '/') {
// 从运算符的容器中移除
ops.remove(i);// 移除当前位置
// 从数字容器中获取对应该运算符位置的两个数字(移除)
BigDecimal d1 = numbers.remove(i);
BigDecimal d2 = numbers.remove(i);
// 运算
d1 = op == '*' ? d1.multiply(d2) : d1.divide(d2,9, BigDecimal.ROUND_HALF_UP) ;
// 把运算结果插入到数字容器中的i位置
numbers.add(i, d1);// 插入到i的位置 原来从i位置一直到最后的数据,都要往后瞬移一位
// numbers.set(i, d1);//设置i位置上的数据为d1,其余不变
i--;// 移除后,后面所有运算符往前移动,为了保证下一个运算符不被遗漏,所以i--
}// end if (op == '*' || op == '/') {
}// end for (int i = 0 ; i < ops.size(); i++) {
// 4. 再加减运算
while (!ops.isEmpty()) {
// 每次去运算容器中第一个运算符
char op = ops.remove(0);
// 每次从数字容器中两次取第一个数字
BigDecimal d1 = numbers.remove(0);
BigDecimal d2 = numbers.remove(0);
// 计算
d1 = op == '+' ? d1.add(d1.add(d2)) : d1.add(d1.subtract(d2));
// 把结果插入到数字容器中的第一个位置
numbers.add(0, d1);
}
// 5. 返回结果
return numbers.get(0);
}
/**
* 从表达式中分离所有的运算符
*
* @param exp
*/
private static List<Character> sliptOperator(String exp) {
List<Character> ops = new ArrayList<Character>();
// -8*-2+3/-1-5*-2-5
// 把真实表达式变成下面的表达式
String formaterExp = formaterExp(exp);
// @8*@2+3/@1-5*@2-5
StringTokenizer st = new StringTokenizer(formaterExp, "@0123456789.");
while (st.hasMoreTokens()) {
String opStr = st.nextElement() + "";// 取出分割符号之间的数据
// System.out.println(numStr);
// 如果前面是@ 变为负数
ops.add(opStr.charAt(0));
}
return ops;
}
/**
* 从表达式中分离所有的数字
*
* @param exp
* -8*-2+3/-1-5*-2-5 表达式
*/
private static List< BigDecimal> sliptNumbers(String exp) {
List<BigDecimal> numbers = new ArrayList<BigDecimal>();
// -8*-2+3/-1-5*-2-5
// 把真实表达式变成下面的表达式
String formaterExp = formaterExp(exp);
// @8*@2+3/@1-5*@2-5
StringTokenizer st = new StringTokenizer(formaterExp, "+-*/");
while (st.hasMoreTokens()) {
String numStr = st.nextElement() + "";// 取出分割符号之间的数据
// System.out.println(numStr);
// 如果前面是@ 变为负数
if (numStr.charAt(0) == '@') {
numStr = "-" + numStr.substring(1);
}
// 把数字型的字符串变成数字
numbers.add(new BigDecimal(numStr));
}
return numbers;
}
private static String formaterExp(String exp) {
int index = 0;
while (index < exp.length()) {
char c = exp.charAt(index);
// 判断是否是-符号
// -代表的是负数 第一个,前一字符*/
if (c == '-') {
// 第一个,
if (index == 0) {
exp = "@" + exp.substring(1);
} else {
// 前一字符* /
if (exp.charAt(index - 1) == '*'
|| exp.charAt(index - 1) == '/') {
exp = exp.substring(0, index) + "@"
+ exp.substring(index + 1);
}
}
}
index++;
}
return exp;
}
}
在遇到除法的时候是通过这行代码限制保留的小数位。
d1 = op == '*' ? d1.add(d1.multiply(d2)) : d1.add(d1.divide(d2,9, BigDecimal.ROUND_HALF_UP)) ;
但是他的是double类型的我改为了BigDecimal类型尽量去避免了精度丢失的问题