Java計(jì)算器

本篇博客將利用“后綴表達(dá)式”,100多行Java代碼(<u>不包括注釋</u>)實(shí)現(xiàn)一個簡單強(qiáng)大的計(jì)算器,支持的運(yùn)算符包括加、減、乘、除、以及小括號。

GitHub代碼鏈接(已經(jīng)做好封裝,可以直接使用)

實(shí)現(xiàn)原理及說明:
先將計(jì)算式變?yōu)槌绦蛉菀子?jì)算的后綴表達(dá)式,然后通過后綴表達(dá)式進(jìn)行計(jì)算得到結(jié)果。
文章分成兩部分進(jìn),第一部分介紹如何將普通的計(jì)算式變?yōu)楹缶Y表達(dá)式,第二部分為程序?qū)崿F(xiàn)。

一、后綴表達(dá)式:
我們平常生活中使用的計(jì)算式是叫中綴表達(dá)式,像這樣8×4-(10+12÷3),變?yōu)楹缶Y表達(dá)式是這樣8 4 × 10 12 3 ÷ + - ,后面這樣的表達(dá)式就便于我們程序計(jì)算了,那么后綴表達(dá)式是如何得出的呢?

生成規(guī)則(需要用到一個棧):

1.遇到操作數(shù),直接輸出;
2.棧為空時,遇到運(yùn)算符,直接入棧;
3.遇到左括號,直接入棧;
4.遇到右括號,右括號不入棧也不輸出,依次彈出棧頂元素直到彈出左括號為止,彈出元素依次輸出,左括號不輸出;
5.遇到其他運(yùn)算符“+”、“-”、“×”、“÷”時,彈出所有優(yōu)先級大于或等于該運(yùn)算符的棧頂元素并輸出,然后將該運(yùn)算符入棧;
6.最終將棧中的剩余元素依次出棧,輸出。

舉一個例子,下面的圖形代表?xiàng)#?/p>

20180819012910718.png

以上就是 “原表達(dá)式(中綴表達(dá)式)” 變?yōu)? “后綴表達(dá)式” 的一種方法。

二、程序?qū)崿F(xiàn)
1、調(diào)用方法:

String result1 = Calculator.input("8×4-(10+12÷3)").getResult();

String result2 = Calculator.input("8/3").getResult(3); // 保留三位小數(shù)結(jié)果

**** GitHub代碼鏈接(已經(jīng)做好封裝,可以直接使用)****

2、全部代碼如下:

package com.csw.calculator;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Stack;

/**
 * 說明:
 *      1.用到了BigDecimal類,它的精度范圍比較高,確保計(jì)算結(jié)果的準(zhǔn)確性。
 *      2.關(guān)于處理負(fù)號,如-5+2,會變?yōu)?-5+2,在負(fù)號前添0。
 * Created by 叢 on 2018/2/12 0012.
 */

public class Calculator {
    private static String formula; // 輸入的計(jì)算式

    private Calculator() {}

    public static Calculator input(String inputFormula) {
        formula = inputFormula;
        return new Calculator();
    }

    /**
     * 獲取結(jié)果,帶有指定保留位數(shù)功能
     * @param accurate 小數(shù)點(diǎn)后保留的位數(shù)
     * @return 經(jīng)過小數(shù)保留后的字符串結(jié)果
     */
    public String getResult(int accurate) {
        if (accurate >= 0)
            return getRawResult().setScale(accurate, RoundingMode.HALF_UP).toPlainString();
        else
            return getResult();
    }

    /**
     * 獲取結(jié)果
     * @return 獲取字符串形式的結(jié)果
     */
    public String getResult() {
        return getRawResult().stripTrailingZeros().toPlainString();
    }

    /**
     * 獲取BigDecimal結(jié)果方法
     * @return 以BigDecimal的形式返回結(jié)果
     */
    public BigDecimal getRawResult() {
        handleFormula(); // 將字符串計(jì)算式中的空格處理掉,為生成后綴表達(dá)式做一些處理。
        getSuffixFormula(); // 生成后綴表達(dá)式計(jì)算公式字符串,便于程序處理。
        return calculate(); // 返回計(jì)算結(jié)果
    }

    /**
     * 將輸入計(jì)算式中的每個數(shù)字以“_”結(jié)尾,表示一個數(shù)字結(jié)束,方便得到后綴表達(dá)式以及計(jì)算結(jié)果
     */
    private void handleFormula() {
        formula = formula.replace(" ", ""); // 處理計(jì)算式中的空格
        formula = formula.replace("×", "*"); // 將×替換為*用于計(jì)算
        formula = formula.replace("÷", "/"); // 將÷替換為/用于計(jì)算
        if (formula.charAt(0) == '-') { // 計(jì)算式以負(fù)號開頭,在前面加0
            formula = "0" + formula;
        }
        // 處理負(fù)號前面是括號的情況
        formula = formula.replace("(-", "(0-");
        // 下面5種運(yùn)算符前可能是數(shù)字,在前面加下劃線"_"
        formula = formula.replace("+", "_+");
        formula = formula.replace("-", "_-");
        formula = formula.replace("*", "_*");
        formula = formula.replace("/", "_/");
        formula = formula.replace(")", "_)");
        formula += "_"; // 計(jì)算式結(jié)尾可能是數(shù)字,結(jié)尾后加"下劃線_"
        // 計(jì)算式如果帶括號,上面加的下劃線可能沒有加在數(shù)字后面,糾正一下
        if (formula.contains("(") || formula.contains(")")) {
            formula = formula.replace(")_+", ")+"); // 加號的左面是括號
            formula = formula.replace(")_-", ")-"); // 減號的左面是括號
            formula = formula.replace(")_*", ")*"); // 乘號的左面是括號
            formula = formula.replace(")_/", ")/"); // 除號的左面是括號
            formula = formula.replace(")_", ")"); // 結(jié)尾是括號
        }
    }

    /**
     * 獲取后綴表達(dá)式,后綴表達(dá)字符串中會保留handleFormula()中的下劃線
     * Stack是Java提供的一個實(shí)現(xiàn)棧效果的類,stack.push(xxx)是入棧,stack.pop(xxx)是出棧,stack.peek()是查看棧頂元素
     */
    private void getSuffixFormula() {
        StringBuilder suffixFormula = new StringBuilder(); // 后綴表達(dá)式字符串
        Stack<Character> stackOperator = new Stack<>();
        for (char c: formula.toCharArray()) { // 將計(jì)算式字符串以char型的方式遍歷一遍
            if (isNumber(c) || c == '_') { // 當(dāng)前char為數(shù)字或下劃線
                suffixFormula.append(c);
            } else if (c == '.') { // 小數(shù)點(diǎn)
                suffixFormula.append(c);
            } else if (c == '+' || c == '-') {
                if (stackOperator.empty()) { // 棧為空
                    stackOperator.push(c);
                }
                else {
                    char c1 = stackOperator.peek();
                    while (c1 != '(') { // 是 +、-、*、/ 符號
                        suffixFormula.append(stackOperator.pop());
                        if (stackOperator.empty())
                            break;
                        c1 = stackOperator.peek();
                    }
                    stackOperator.push(c);
                }
            } else if (c == '*' || c == '/') {
                if (stackOperator.empty()) {
                    stackOperator.push(c);
                }
                else {
                    char c1 = stackOperator.peek();
                    while (c1 == '*' || c1 == '/') {
                        suffixFormula.append(stackOperator.pop());
                        if (stackOperator.empty())
                            break;
                        c1 = stackOperator.peek();
                    }
                    stackOperator.push(c);
                }
            } else if (c == '(') {
                stackOperator.push(c);
            } else if (c == ')') { // 不需形成數(shù)字添加_,因?yàn)?) 前肯定是數(shù)字,后肯定是運(yùn)算符,形成數(shù)字交給后面的運(yùn)算符處理
                char c1 = stackOperator.peek();
                while (c1 != '(') {
                    suffixFormula.append(stackOperator.pop());
                    if (stackOperator.empty())
                        break;
                    c1 = stackOperator.peek();
                }
                stackOperator.pop(); // 彈出“(”
            }
        }
        while (!stackOperator.empty())
            suffixFormula.append(stackOperator.pop());
        formula = suffixFormula.toString(); // 將后綴表達(dá)式賦予全局計(jì)算式,后綴表達(dá)式中會保留下劃線
    }

    /**
     * 最后的計(jì)算方法,得到計(jì)算結(jié)果
     */
    private BigDecimal calculate() {
        Stack<BigDecimal> stackNumber = new Stack<>();
        Stack<Character> stackSingleNumber = new Stack<>();
        int intBit = 1; // 整數(shù)部分位數(shù)
        int dotBit = 0; // 小數(shù)部分位數(shù)
        for (char c: formula.toCharArray()) {
            if (isNumber(c) || c == '.') {
                stackSingleNumber.push(c);
            } else if (c == '_') {
                BigDecimal b = new BigDecimal(String.valueOf(stackSingleNumber.pop() - 48)); // char轉(zhuǎn)int

                while (!stackSingleNumber.empty()) {
                    char c1 = stackSingleNumber.pop();
                    if (c1 != '.') {                // char轉(zhuǎn)int
                        b = b.add(new BigDecimal(String.valueOf(c1 - 48)).multiply(new BigDecimal("10").pow(intBit)));
                        intBit++;
                    } else {
                        dotBit = intBit;
                    }
                }
                if (dotBit != 0)
                    b = b.multiply(new BigDecimal("0.1").pow(dotBit));
                stackNumber.push(b);
                // 初始化位數(shù)變量
                intBit = 1;
                dotBit = 0;
            } else if (c == '+') {
                BigDecimal b1 = stackNumber.pop();
                BigDecimal b2 = stackNumber.pop();
                stackNumber.push(b2.add(b1));
            } else if (c == '-') {
                BigDecimal b1 = stackNumber.pop();
                BigDecimal b2 = stackNumber.pop();
                stackNumber.push(b2.subtract(b1));
            } else if (c == '*') {
                BigDecimal b1 = stackNumber.pop();
                BigDecimal b2 = stackNumber.pop();
                stackNumber.push(b2.multiply(b1));
            } else if (c == '/') {
                BigDecimal b1 = stackNumber.pop();
                BigDecimal b2 = stackNumber.pop();
                stackNumber.push(b2.divide(b1, MathContext.DECIMAL128));
            }
        }
        return stackNumber.pop();
    }

    /**
     * 判斷傳入的char是否為數(shù)字
     */
    private boolean isNumber(char c) {
        return (c >= 48 && c <= 57);
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容