计算解析四则运算表达式【带负号和括号】

一.简述

使用正则匹配加递归实现四则混合运算的(包括符号)的解析以及计算–实现语言Java

第一次写博客,看客们多多见谅。

也是一天晚上,和同学百无聊奈之时谈到这个问题,我是个实在的人的。
说写就写,总共从思考到编码到完全调试成功历时两个多小时,所以拿出来分享一下。

总体思想:因为所有表达式括号的作用无非就是要提高优先级而已,所以我们的目标就在于两个
1.去括号

2.计算括号里面的表达式【式子里面不带括号】

3.从括号的最内层开始计算,计算出结果去括号并替换计算值。直到计算完毕

4.处理正负数【附加】

二、关于以上三个问题,我们决定先解决掉第二个问题**

如上式子: 1+2-3*4/5
解决方案步骤如下:
1.分离开操作数和操作符
申请两个集合List(一个为String类型–存符号 一个为Double【此处选择用double类型主要是为了偷懒,计算各种小数的问题】–存数字)。所以拆分的结果为:

  • String类型的–{+,-,*,/},存的是操作符,用a,b,c,d分别代替+,-,*,/
  • Double类型的–{1,2,3,4,5},存的是操作数。

2.解决负数问题
因为表达式如果想成立,负数必须在第一个。如 -4+5-6*7/8 毕竟不可能为 4+-5-6*7/8那么,有人会说可能写成这样不就行了 4+(-5)-6*7/8 但是不行,因为我们解决的 第二个问题式子里面是不带括号的,所以不存在该情况。因此,我会在遇到这种表达式的时候使用替换为 0-4+5-6*7/8,即在式子最前面添上 “0”【因为0加任何数都不变,-4即为0-4】。

  • 代码片段
if (calStr.charAt(0) == 'a' || calStr.charAt(0) == 'b') {
    //如果 遇到减号(-) 就替换成 (@) 因为计算机不能知道他是减号还是负号
    calStr = "0"+calStr.substring(1);
}

3.计算
此时已经有了一个存符号【操作符】的集合和一个数字【操作数】的集合,且解决掉了负号的问题。

  • 先算乘法或者除法。计算过称为:
    遍历两个集合,有乘除就算乘除,找到符号集合中乘除号的位置,假设为i,则可以知道两个数字【操作数】的位置为i和i+1。
  • 代码片段:
if ("a".equals(calStrArr.get(index))) {
    result = numberArr.get(index)+numberArr.get(index+1);
}else if ("b".equals(calStrArr.get(index))) {
    result = numberArr.get(index)-numberArr.get(index+1);
}else if ("c".equals(calStrArr.get(index))) {
    result = numberArr.get(index)*numberArr.get(index+1);
}else if("d".equals(calStrArr.get(index))){
    result = numberArr.get(index)/numberArr.get(index+1);
}
//a,b,c,d分别为+-*/
  • 对计算后的结果在两个集合中替换相关,递归继续重复,直到算出最终结果【标志为–符合集合没有符号】

三、去括号

1.去括号的思想也就是,从最里层的括号开始依次计算出结果替换掉括号。思想也是递归–具体不做详述,见代码,代码有详尽的注释。
2.重要的点在于去括号,如下例子表达式1*(3-5),我们算出3-5=(-2),所以式子被替换为 1*(-2) ,那么此处不可直接去括号因为1*-2不合法。所以我的做法是将-2替换为一个特殊的字符如1*@2,将-号替换为@。然后在计算时候对这种情况做处理

  • 代码片段:
String result_ = result>0?(result+""):("@"+(result__.substring(1)));

到此为止,已经计算解析出相关的表达式。期间肯定有不合理之处,希望大家提出来。初次写博客,不当之处,请多原谅下面贴出代码:

  • 主要计算过程类:
package com.ylink.util;

import java.util.ArrayList;
import java.util.List;

public class NewOpr {
    //计算不带括号的加减乘除
    public double calBase(String calStr){
        return DiffcultCal(calStr);
    }

    /**
     * 替换 +-*chu 符号 分别为a,b,c,d
     * @param calStr
     * @return
     */
    public String replace(String calStr){
        calStr = calStr.replace("+", "a");
        calStr = calStr.replace("-", "b");
        calStr = calStr.replace("*", "c");
        calStr = calStr.replace("/", "d");
        return calStr;
    }

    /**
     * 假若表达式: -1+2*3
     * 
     * 主要是拆分,将一个简单的表达式(不含括号) 差分成 一个专门存符号的集合{+,*}和一个专门存 操作数的集合{-1,2,3}
     * 其中一个细节就是对复数的处理,如-1
     * @param calStr
     * @return
     */
    public ModelArr splitCalStr(String calStr){
        ModelArr modelArr = new ModelArr();
        List<Double> numberArr = new ArrayList<Double>();
        List<String> calStrArr = new ArrayList<String>();

        //对复数的处理
        //如果  有 负号的 情况有三种
        //1. -4
        //2. -4+5
        //3. -4+5 他其实就是(-4加5  但是如果写成   -4+5则不能解析。因此加0变为 0-4+5)
        //负号一定为 表达式的第一个
        if (calStr.charAt(0) == 'a' || calStr.charAt(0) == 'b') {
            //如果 遇到减号(-) 就替换成 (@) 因为计算机不能知道他是减号还是负号
            calStr = "0"+calStr.substring(1);
        }

        String regex = "[a-d]";
        String[] numberArr_ = calStr.split(regex);
        for (int i = 0; i < numberArr_.length; i++) {
            /*
             * 在拆分的时候,如果遇到 
             */
            if (numberArr_[i].startsWith("@")) {
                numberArr.add(0-Double.parseDouble(numberArr_[i].substring(1)));
                continue;
            }
            numberArr.add(Double.parseDouble(numberArr_[i]));
        }

        for (int i = 0; i < calStr.length(); i++) {
            if (calStr.charAt(i) == 'a' || calStr.charAt(i) == 'b' || calStr.charAt(i) == 'c' || calStr.charAt(i) == 'd') {
                calStrArr.add(""+calStr.charAt(i));
            }
        }
        modelArr.setNumberArr(numberArr);
        modelArr.setCalStrArr(calStrArr);

//      modelArr
        return modelArr;
    }

    /**
     * 对 不带括号的加减乘除 运算表达式(已经被拆分成 一个符号集合和一个值集合)进行计算
     * @param modelArr
     * @return
     */
    public double baseCal(ModelArr modelArr){
        List<String> calStrArr = modelArr.getCalStrArr();
        List<Double> numberArr = modelArr.getNumberArr();
        //判断符号集合是不是空了且值集合也只有一个操作数。如果是,则说明已经计算出结果。否则计算该表达式
        if ((calStrArr == null || calStrArr.size() == 0) && numberArr.size() == 1) {
            return numberArr.get(0);
        }else{
            double result = Double.MIN_VALUE; 
            int index = isHave_cd(calStrArr);
            if (index == -1) {
                index = 0;
            }
            if ("a".equals(calStrArr.get(index))) {
                result = numberArr.get(index)+numberArr.get(index+1);
            }else if ("b".equals(calStrArr.get(index))) {
                result = numberArr.get(index)-numberArr.get(index+1);
            }else if ("c".equals(calStrArr.get(index))) {
                result = numberArr.get(index)*numberArr.get(index+1);
            }else if("d".equals(calStrArr.get(index))){
                result = numberArr.get(index)/numberArr.get(index+1);
            }
            modelArr.setCalStrArr(doCalCompleteForCalStrArr(calStrArr,index));
            modelArr.setNumberArr(doCalCompleteForNumberArr(numberArr,index,result));
            return baseCal(modelArr);
        }
    }


    /**
     * 带括号 的加减乘除
     */
    public double DiffcultCal(String calStr){
        int start = findLastLeftKH(calStr);
        int end = findNearLeftKH(calStr,start);
        //如果不存在括号,则直接计算。否则递归调用
        if (start == -1 || end == -1) {
            //替换符号
            calStr = replace(calStr);
            //拆分表达式
            ModelArr modelArr = splitCalStr(calStr);
            //计算结果
            return baseCal(modelArr);
        }else{
            //拿到括号 "包起来"的表达式。
            String calStr_ = calStr.substring(start+1, end);
            //替换符号
            calStr_ = replace(calStr_);
            //拆分表达式
            ModelArr modelArr = splitCalStr(calStr_);
            //计算
            double result = baseCal(modelArr);
            //处理计算的负数,然后去括号重新生成新的表达式
            //如果 遇到减号(-) 就替换成 (@) 因为计算机不能知道他是减号还是负号
            //切记此处,如果  得到的 值是负值。应加上括号。否则如:1+(3-5)  计算之后3-5=-2  就变成1+-2就错了。所以 我么可以写成 1+@2 以后开到@2就转换成-2
            String result__ = result+""; //此处得到 -2
            //("@"+(result__.substring(1))) 得到@2, 若是直接@+result则得到 @-2
            String result_ = result>0?(result+""):("@"+(result__.substring(1)));
            //计算完结果,将结果替换到 相应的表达式中。形成相应的新的表达式。--主要作用是去括号
            //那么此处为什么,不用以前的法子用  0-2 替换成-2。因为假设表达式为10*(3-4) 按照以前替换就会变为 10*0-1 而正确
            //则应该是10*(0-1),那么肯定会有人说加上括号不就完了,但是我们的目的是为了去括号,你去掉了括号又加了括号。是无用功
            calStr = calStr.substring(0,start)+result_+calStr.substring(end+1);

            //递归调用
            return DiffcultCal(calStr);
        }
    }

    /**
     * 是否还存在乘法和除法 
     * @return  -1代表不存在,否则返回相关的下标
     */
    public int isHave_cd(List<String> calStrArr){
        for (int i = 0; i < calStrArr.size(); i++) {
            if ("c".equals(calStrArr.get(i)) || "d".equals(calStrArr.get(i))) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 算完数据之后,将 符号数组 的相应数据删除
     * @param calStrArr
     * @param index
     * @return
     */
    public List<String> doCalCompleteForCalStrArr(List<String> calStrArr,int index){

        calStrArr.remove(index);

        return calStrArr;
    }

    /**
     * 算完数据之后,将 数组数组 的相应数据删除
     * @param numberArr
     * @param index
     * @return
     */
    public List<Double> doCalCompleteForNumberArr(List<Double> numberArr,int index,Double result){
        numberArr.set(index, result);
        numberArr.remove(index+1);
        return numberArr;
    }

    /**
     * 从左往右  最后一个左括号的位置
     * 
     * -1代表没有
     * 
     * @return
     */
    public int findLastLeftKH(String calStr){
        return calStr.lastIndexOf("(");
    }


    /**
     * 找对应的左括号,的最近的一个右括号
     * 
     * -1代表没有
     * 
     * @return
     */
    public int findNearLeftKH(String calStr,int leftIndex){
        return calStr.indexOf(")", leftIndex);
    }
}
  • 一个封装的Bean,主要是为了参数好传递
package com.ylink.util;

import java.util.List;

/**
 * 用于存放 拆分 的表达式
 * @author Administrator
 *
 */
public class ModelArr {
    //存值
    private List<Double> numberArr;
    //存符号
    private List<String> calStrArr;
    public List<Double> getNumberArr() {
        return numberArr;
    }
    public void setNumberArr(List<Double> numberArr) {
        this.numberArr = numberArr;
    }
    public List<String> getCalStrArr() {
        return calStrArr;
    }
    public void setCalStrArr(List<String> calStrArr) {
        this.calStrArr = calStrArr;
    }


}
  • 测试类:
package com.ylink.util;

public class Test {
    public static void main(String[] args) {
        try {
            String calStr = "((-1)*3/(-3+1)*(-1.0-0.000))";
//          String calStr = "-(-1.0)";
            System.out.println("表达式为:"+calStr);
            double result = new NewOpr().calBase(calStr);
            System.out.println("计算结果为:"+result);
        } catch (Exception e) {
            System.out.println("表达式不正确..."+e);
        }
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值