Java400行代码的半科学计算机(括号+小数功能)【链表】

该代码是字符串输入字符串返回,本身开发是应用于安卓应用,所以没有设计图形界面,完全是逻辑计算,开始是打算一个方法写到底的,但是写起来不容易读,所以重构了一次,主要数据类型用的是链表(没有使用Stack运算),对是我故意的,虽然代码不怎么好,但每个单元还是做了单元测试的,暂时还没发现bug,但不保证没有。把注释去了刚好400行,我数过了,应该不算多,当然里面部分链表使用了for i循环,虽然影响性能,但转换也挺费性能的,部分能转的也转了foreach。但考虑到计算器能的消耗,就不用麻烦自己了。

package com.ca;

import java.math.BigDecimal;
import java.util.LinkedList;
import static java.util.regex.Pattern.matches;

/**
 * 实现带括号和小数计算运算
 * 优先运算符号和数学方法一致
 * 小数最多保留8位
 * 小数只有有效精度
 */
public class Caculator {
    //遍历数组时不能改变数组长度,这里记录需要添加的外层括号数量
    private int pdCountS = 0;
    private int pdCountE = 0;

    /**
     * 对外接口
     * @param string 字符串类型的公式,英文符号,不能使用空格
     * @return 结果以字符串类型返回
     */
    public static String  run(String string){

        return new Caculator().start(string);
    }

    /**
     * 主方法入口
     * @param string 从唯一接口获取数据
     * @return  结果返回接口
     */
    private String start(String string){
        LinkedList<String> asp = conversion(string);
        asp=pdDeal(asp);
        for (int i = 0; i <asp.size() ; i++) {
            if (matches(".*\\(.*", asp.get(i))) {
                asp=replace(asp);
                i=-1;
            }
        }
        return  read(asp);
    }

    /**
     * @param string 控制器接收的字符串类型
     * @return LinkedList<String> 下级
     */
    private LinkedList<String> conversion(String string) {                   //字符串转链表
        LinkedList<String> asm = new LinkedList<>();
        LinkedList<String> as2 = new LinkedList<>();
        String[] arr1 = string.split("\\+|-|\\*|/|\\(|\\)");  //读取String数字部分
        String[] arr2 = string.split("\\d|\\.");                //读取String运算符部分
        for (String i : arr1) {       //清空空值
            if (!"".equals(i)) {
                asm.add(i);
            }
        }
        for (String i : arr2) {      //清空空值
            if (!"".equals(i)) {
                if (matches(".*[() ].*+", i) ) {
                    System.err.println("输入格式错误:本机不接受非英文字符和括号");
                    System.exit(1);
                }
                as2.add(i);
            }
        }
        try {
            if (!matches("^\\(.+|\\(", as2.get(0))) {           //数字和运算符合并,开头不是“(”时
                for (int i = 0; i < as2.size(); i++) {
                    asm.add(2 * i + 1, as2.get(i));
                }
                return asm;
            }
            for (int i = 0; i < as2.size(); i++) {               //数字和运算符合并,开头为数字时
                asm.add(2 * i, as2.get(i));
            }
        }catch (IndexOutOfBoundsException ex){
            System.err.println("输入异常:请输入正确符号,不要输入英文类型的.()运算符和数字以外的字符");
            System.exit(1);
        }
        return asm;
    }

    /**
     * 读取计算无括号内容
     * @param as1 任何输入的无空格链表
     * @return  字符串类型结果
     */
    private String read(LinkedList<String> as1){
        int size =0;
        String result = "";
        String start = "";
        String end = "";
        String nums = "";
        String nume = "";
        for (String i:as1) {
            if ( !matches("\\D", i)){   //String为数字时
                if ("".equals(nums)) {
                    nums=i;
                }
                else nume=i;
            }
            else if ("".equals(start)) {             //String为运算符时
                start=i;
            }
            else end=i;
            size++;
            if (!"".equals(end) ||size==as1.size()) {
                result=judgmentC(start,nums,nume);
                start = end;
                end ="";
                nume="";
                nums=String.valueOf(result);
            }
        }
        return result;
    }

    /**
     * 四则运算
     * @param start 运算符
     * @param nums  运算符左侧数字
     * @param nume  运算符右侧数字
     * @return 字符串类型结果
     */
    private String judgmentC(String start, String nums, String nume) {
        BigDecimal b1 = new BigDecimal(nums);
        BigDecimal b2 = new BigDecimal(nume);
        String re="";
        if ("+".equals(start)) {
            re = b1.add(b2).toString();
        }
        if ("-".equals(start)) {
            re =b1.subtract(b2).toString();
        }
        if ("*".equals(start)) {
            re =b1.multiply(b2).toString();
        }
        if ("/".equals(start)) {
            re =b1.divide(b2,8, BigDecimal.ROUND_HALF_EVEN).toString();
            String div = "";
            int len = re.length();
            int lencount = 0;
            for (int i = len-1; i > 0; i--) {
                if (re.charAt(i)=='0') {
                    lencount++;
                    continue;
                }
                break;
            }
            for (int i = 0; i < len-lencount; i++) {
                div += re.charAt(i);
                if (re.charAt(len-lencount-1) == '.') {
                    break;
                }
            }
            re = div;
        }
        return re;
    }

    /**
     * 括号内优先运算
     * @param asm Tset1单元传入带括号
     * @return  消除括号后返回括号内结果
     */
    private LinkedList<String> replace(LinkedList<String> asm){
        LinkedList<String> as2 = new LinkedList<>();
        int count1 = 0;    //( 号的数量
        int count2 = 0;    // )号的数量
        int key1 = 0;      //锁1,第一个括号处理完后打开
        int key2 = 0;      //锁2,检测到第一个括号时打开
        int index=0;       //指针,第一个 ( 括号出现时对应的i位置

        //解最外圈括号并添加到as2
        for (int i = 0; i < asm.size()-1; i++) {
            if (key2 != 1 && matches(".*\\(.*", asm.get(i))) {
                key2=1;
                index=i;
            }
            //静态锁2打开后开始添加到as2
            if (key2 == 1) {
                as2.add(asm.get(i));
            }
            //拆外圈括号
            if (matches(".*\\(.*", asm.get(i))) {
                int cmt1=0;
                for (int j = 0; j < asm.get(i).length(); j++) {
                    if (asm.get(i).charAt(j) == '(') {
                        cmt1++;
                    }
                }
                count1 += cmt1;
                if (key1 != 1) {
                    asm.set(i, asm.get(i).replaceFirst("\\(", ""));
                }
                key1=1;
            }
            if (matches(".*\\).*", asm.get(i + 1))) {
                int cmt1=0;
                int a=asm.get(i+1).length();
                for (int j = 0; j < a; j++) {
                    if (asm.get(i+1).charAt(j) == ')') {
                        cmt1++;
                    }
                }
                count2 += cmt1;
                if (count1 == count2) {
                    as2.add(asm.get(i+1));
                    asm.set(i + 1, asm.get(i + 1).replaceFirst("\\)", ""));
                }
            }

            if (count1 == count2 && count1 != 0) {
                int index2=0;
                if (matches(".*\\(.*",as2.get(0))) {
                    int countS = 0;
                    int countE = 0;
                    for (int j = 0; j < as2.get(0).length(); j++) {
                        if (as2.get(0).charAt(j)=='(') {
                            countS++;
                        }
                    }
                    for (int j = 0; j < as2.get(as2.size()-1).length(); j++) {
                        if (as2.get(as2.size()-1).charAt(j)==')') {
                            countE++;
                        }
                    }
                    if (countE > 1 && countS > 1) {
                        return asm;
                    }
                }

                if ("".equals(asm.get(asm.size()-1))) {
                    asm.remove(asm.size()-1);
                }
                if ("".equals(asm.get(0))) {
                    asm.remove(0);
                    index2=1;
                }
                if (asm.size() < as2.size()) {
                    return asm;
                }
                if (as2 !=null) {
                    asm=asmReduce(asm,as2,index,index2);
                    return asm;
                }
                break;
            }
        }
        if ("".equals(asm.get(asm.size()-1))) {
            asm.remove(asm.size()-1);
        }
        return asm;
    }

    /**
     * 仅供replace方法调用
     * 收缩准备返回的主链表
     * @param asm replace方法中的asm链表
     * @return  修改后返回asm链表
     */
    private LinkedList<String> asmReduce(LinkedList<String> asm,LinkedList<String> as2,int index,int index2){
        for (int j = 1; j <as2.size()-1-index2; j++) {
            asm.remove(index+1-index2);
        }
        if (matches(".*\\(.*", asm.get(index))) {
            asm.set(index, asm.get(index).replaceAll("\\(", ""));
        }
        if (asm.size()>index+1 && matches(".*\\).*", asm.get(index+1))) {
            asm.set(index+1, asm.get(index+1).replaceAll("\\)", ""));
        }
        as2=as2Reduce(as2);
        for (int j = 0; j < as2.size(); j++) {
            if (matches(".*\\(.*", as2.get(j))) {
                as2=replace(as2);
                if (index+1-index2==asm.size()) {
                    asm.add(read(as2));
                    return asm;
                }
                if ("".equals(asm.get(0))) {
                    asm.set(0,read(as2));
                    return asm;
                }
                if (index+1-index2>=asm.size() || matches(".*\\d.*||.* .*", asm.get(index+1-index2))) {
                    asm.set(index+1-index2,read(as2));
                }
                else {
                    asm.add(index+1-index2,read(as2));
                }
                return asm;
            }
        }
        if (index+1-index2 >= asm.size()) {
            asm.add(read(as2));
        }
        else if (index+1-index2 ==0) {
            asm.set(index+1-index2,read(as2));
        }
        else {
            asm.add(index+1-index2,read(as2));
        }
        return asm;
    }

    /**
     * 仅供replace方法调用
     * as2为需要计算的括号内部分
     * @param as2 replace 中的as2
     * @return 返回下一步结果
     */
    private LinkedList<String> as2Reduce(LinkedList<String> as2){
        if (matches(".*\\).*", as2.get(as2.size()-1))) {
            as2.set(as2.size()-1, as2.get(as2.size()-1).replaceFirst("\\)", ""));
            as2.set(as2.size()-1, as2.get(as2.size()-1).replaceAll("[^)]", ""));
        }
        if (!matches(".*\\).*", as2.get(as2.size()-1))) {
            as2.remove(as2.size()-1);
        }

        if (matches(".*\\(.*", as2.get(0))) {
            as2.set(0, as2.get(0).replaceFirst("\\(", ""));
            as2.set(0, as2.get(0).replaceAll("[^(]", ""));
        }
        if (!matches(".*\\(.*", as2.get(0))) {
            as2.remove(0);
        }
        return as2;
    }

    /**
     * 对/*号提供括号实现优先级
     * @param asp  conversion方法后
     * @return  添加括号后返回
     */
    private LinkedList<String> pdDeal(LinkedList<String> asp){
        int counti =0;      //记录每次循环的对应位置
        int count1 = 0;     //记录(数
        int count2 = 0;     //记录)数
        for (String i:asp) {
            if (matches(".*\\*.*|.*/.*", i)){
                if (matches(".*\\).*", i)){
                    for (int j = 0; j <i.length() ; j++) {
                        if (i.charAt(j)==')') {
                            count1++;
                        }
                    }
                }
                if (matches(".*\\(.*", i)){
                    for (int j = 0; j <i.length() ; j++) {
                        if (i.charAt(j)=='(') {
                            count2++;
                        }
                    }
                }
                asp = pdAdd(asp,counti,count1,count2);
                count1=0;
                count2=0;
            }
            counti++;
        }
        String end ="";
        String start ="";
        for (int i = 0; i < pdCountS; i++) {
            start +="(";
        }
        for (int i = 0; i < pdCountE; i++) {
            end +=")";
        }
        if (!"".equals(start)) {
            asp.add(0,start);
        }
        if (!"".equals(end)) {
            asp.add(end);
        }
        return asp;
    }

    /**
     * 仅供pdeal方法调用
     * @param asp       传递链表
     * @param counti    优先级符号出现的位置
     * @param count1    该位置已有的)号数量
     * @param count2    该位置已有的(号数量
     * @return          返回添加距哦好后结果
     */
    private LinkedList<String> pdAdd(LinkedList<String> asp,int counti,int count1,int count2){
        int countL = 0;
        int countR = 0;
        int aLi = 0;
        int aRi = 0;
        for (int i = counti-1; i >= 0; i--) {
            if (matches(".*\\(.*", asp.get(i))){
                for (int j = 0; j <asp.get(i).length() ; j++) {
                    if (asp.get(i).charAt(j)=='(') {
                        countL++;
                    }
                    if (count1 == countL) {
                        aLi=1;
                        break;
                    }
                }
            }
            if (aLi != 1 && matches(".*\\).*", asp.get(i))){
                for (int j = 0; j <asp.get(i).length() ; j++) {
                    if (asp.get(i).charAt(j)==')') {
                        count1++;
                    }
                }
            }
            if (aLi == 1||count1==0) {
                if (i == 0 && !matches(".*\\(.*", asp.get(0))) {
                    pdCountS++;
                    break;
                }
                if (count1 == 0) {
                    asp.set(i-1,asp.get(i-1)+"(");
                }
                else {
                    asp.set(i,asp.get(i)+"(");
                }
                break;
            }
            if (i == 0) {
                pdCountS++;
            }
        }

        for (int i = counti+1; i < asp.size(); i++) {
            if (matches(".*\\).*", asp.get(i))){
                for (int j = 0; j <asp.get(i).length() ; j++) {
                    if (asp.get(i).charAt(j)==')') {
                        countR++;
                    }
                    if (count2 == countR) {
                        aRi=1;
                        break;
                    }
                }
            }
            if (aRi !=1 && matches(".*\\(.*", asp.get(i))){
                for (int j = 0; j <asp.get(i).length() ; j++) {
                    if (asp.get(i).charAt(j)=='(') {
                        count2++;
                    }
                }
            }
            if (aRi==1||count2==0) {
                if (i+1 == asp.size() && !matches(".*\\).*", asp.get(i))) {
                    pdCountE++;
                    break;
                }
                if (count2 == 0) {
                    asp.set(i+1,")"+asp.get(i+1));
                    break;
                }
                else {
                    asp.set(i,")"+asp.get(i));
                }
                return asp;
            }
        }
        return asp;
    }
    public static void main(String[] args) {
        String a="5/(6-5)+(9.5+(9.6-4.2))";
        System.out.println(run(a));
    }
}


 
 

猜你喜欢

转载自blog.csdn.net/m15682532244/article/details/78969692