Java 計算數學表達式(字符串解析求值工具)


Java字符串轉換成算術表達式計算並輸出結果,通過這個工具可以直接對字符串形式的算術表達式進行運算,並且使用非常簡單。

這個工具中包含兩個類 Calculator 和 ArithHelper

Calculator 代碼如下:

import java.util.Collections;
import java.util.Stack;

/**
 *  算數表達式求值 
 *  直接調用Calculator的類方法conversion() 
 *  傳入算數表達式,將返回一個浮點值結果
 *  如果計算過程錯誤,將返回一個NaN
 */
public class Calculator {
    private Stack<String> postfixStack = new Stack<String>();// 后綴式棧
    private Stack<Character> opStack = new Stack<Character>();// 運算符棧
    private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 運用運算符ASCII碼-40做索引的運算符優先級

    public static double conversion(String expression) {
        double result = 0;
        Calculator cal = new Calculator();
        try {
            expression = transform(expression);
            result = cal.calculate(expression);
        } catch (Exception e) {
            // e.printStackTrace();
            // 運算錯誤返回NaN
            return 0.0 / 0.0;
        }
        // return new String().valueOf(result);
        return result;
    }

    /**
     * 將表達式中負數的符號更改
     *
     * @param expression
     *            例如-2+-1*(-3E-2)-(-1) 被轉為 ~2+~1*(~3E~2)-(~1)
     * @return
     */
    private static String transform(String expression) {
        char[] arr = expression.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == '-') {
                if (i == 0) {
                    arr[i] = '~';
                } else {
                    char c = arr[i - 1];
                    if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {
                        arr[i] = '~';
                    }
                }
            }
        }
        if(arr[0]=='~'||arr[1]=='('){
            arr[0]='-';
            return "0"+new String(arr);
        }else{
            return new String(arr);
        }
    }

    /**
     * 按照給定的表達式計算
     *
     * @param expression
     *            要計算的表達式例如:5+12*(3+5)/7
     * @return
     */
    public double calculate(String expression) {
        Stack<String> resultStack = new Stack<String>();
        prepare(expression);
        Collections.reverse(postfixStack);// 將后綴式棧反轉
        String firstValue, secondValue, currentValue;// 參與計算的第一個值,第二個值和算術運算符
        while (!postfixStack.isEmpty()) {
            currentValue = postfixStack.pop();
            if (!isOperator(currentValue.charAt(0))) {// 如果不是運算符則存入操作數棧中
                currentValue = currentValue.replace("~", "-");
                resultStack.push(currentValue);
            } else {// 如果是運算符則從操作數棧中取兩個值和該數值一起參與運算
                secondValue = resultStack.pop();
                firstValue = resultStack.pop();

                // 將負數標記符改為負號
                firstValue = firstValue.replace("~", "-");
                secondValue = secondValue.replace("~", "-");

                String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
                resultStack.push(tempResult);
            }
        }
            return Double.valueOf(resultStack.pop());
    }

    /**
     * 數據准備階段將表達式轉換成為后綴式棧
     * 
     * @param expression
     */
    private void prepare(String expression) {
        opStack.push(',');// 運算符放入棧底元素逗號,此符號優先級最低
        char[] arr = expression.toCharArray();
        int currentIndex = 0;// 當前字符的位置
        int count = 0;// 上次算術運算符到本次算術運算符的字符的長度便於或者之間的數值
        char currentOp, peekOp;// 當前操作符和棧頂操作符
        for (int i = 0; i < arr.length; i++) {
            currentOp = arr[i];
            if (isOperator(currentOp)) {// 如果當前字符是運算符
                if (count > 0) {
                    postfixStack.push(new String(arr, currentIndex, count));// 取兩個運算符之間的數字
                }
                peekOp = opStack.peek();
                if (currentOp == ')') {// 遇到反括號則將運算符棧中的元素移除到后綴式棧中直到遇到左括號
                    while (opStack.peek() != '(') {
                        postfixStack.push(String.valueOf(opStack.pop()));
                    }
                    opStack.pop();
                } else {
                    while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
                        postfixStack.push(String.valueOf(opStack.pop()));
                        peekOp = opStack.peek();
                    }
                    opStack.push(currentOp);
                }
                count = 0;
                currentIndex = i + 1;
            } else {
                count++;
            }
        }
        if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一個字符不是括號或者其他運算符的則加入后綴式棧中
            postfixStack.push(new String(arr, currentIndex, count));
        }

        while (opStack.peek() != ',') {
            postfixStack.push(String.valueOf(opStack.pop()));// 將操作符棧中的剩余的元素添加到后綴式棧中
        }
    }

    /**
     * 判斷是否為算術符號
     *
     * @param c
     * @return
     */
    private boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
    }

    /**
     * 利用ASCII碼-40做下標去算術符號優先級
     *
     * @param cur
     * @param peek
     * @return
     */
    public boolean compare(char cur, char peek) {// 如果是peek優先級高於cur,返回true,默認都是peek優先級要低
        boolean result = false;
        if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
            result = true;
        }
        return result;
    }

    /**
     * 按照給定的算術運算符做計算
     *
     * @param firstValue
     * @param secondValue
     * @param currentOp
     * @return
     */
    private String calculate(String firstValue, String secondValue, char currentOp) {
        String result = "";
        switch (currentOp) {
        case '+':
            result = String.valueOf(ArithHelper.add(firstValue, secondValue));
            break;
        case '-':
            result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
            break;
        case '*':
            result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
            break;
        case '/':
            result = String.valueOf(ArithHelper.div(firstValue, secondValue));
            break;
        }
        return result;
    }
}
Calculator

ArithHelper 代碼如下:

public class ArithHelper {

    // 默認除法運算精度
    private static final int DEF_DIV_SCALE = 16;

    // 這個類不能實例化
    private ArithHelper() {
    }

    /**
     * 提供精確的加法運算。
     *
     * @param v1 被加數
     * @param v2 加數
     * @return 兩個參數的和
     */

    public static double add(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    public static double add(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精確的減法運算。
     *
     * @param v1 被減數
     * @param v2 減數
     * @return 兩個參數的差
     */

    public static double sub(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    public static double sub(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精確的乘法運算。
     *
     * @param v1
     *            被乘數
     * @param v2
     *            乘數
     * @return 兩個參數的積
     */

    public static double mul(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static double mul(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到 小數點以后10位,以后的數字四舍五入。
     *
     * @param v1
     *            被除數
     * @param v2
     *            除數
     * @return 兩個參數的商
     */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    public static double div(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指 定精度,以后的數字四舍五入。
     *
     * @param v1 被除數
     * @param v2 除數
     * @param scale 表示表示需要精確到小數點以后幾位。
     * @return 兩個參數的商
     */

    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精確的小數位四舍五入處理。
     *
     * @param v 需要四舍五入的數字
     * @param scale 小數點后保留幾位
     * @return 四舍五入后的結果
     */

    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static double round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(v);
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}
ArithHelper

使用時調用 Calculator 類的 conversion()方法,並傳入算術表達式參數,即可返回一個 Double 類型的值。

使用示例:

 1 public class MathTest {
 2 
 3      public static void main(String[] args) {
 4      String expression = "(0*1--3)-5/-4-(3*(-2.13))";
 5      double result = Calculator.conversion(expression);
 6      System.out.println(expression + " = " + result);
 7      System.out.println();
 8      }
 9      
10 }

控制台輸出:

(0*1--3)-5/-4-(3*(-2.13)) = 10.64

測試截圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM