使用Java寫公式計算器
我的一位好友需要軟體裡面內建一個小外掛,可以根據使用者輸入的簡單公式引導使用者進行輸入和計算,所以簡單地寫了一個原理實現。主要用到Java字串處理和資料結構的棧思想,難度不大但要非常細心。
原理如下:
1、對公式以等號為分隔符進行左右分割,然後取得公式右邊字串
2、對公式右邊字串進行運算子(+、-、*、/、(、))和常數的去除,然後分割出變數名
3、請求使用者輸入各變數的值,用使用者輸入值取代字串的對應變數,使得公式變成純數字和純運算子構成的字串。
4、最複雜的一環:尋找第一個右括號(想到於找到棧頂),然後倒過來找左括號(如公式格式正確則必然同時是最後一個左括號),左括號和右括號作為一個整體提取作為子字串。子字串裡面先找乘法和除法的子子字串進行計算,再找加法和除法的子子字串進行計算(按運算子查詢,並找到符號兩邊的數值),然後運算完之後替換回去作為新的子字串,直到子字串沒有運算子則結束。
5、子字串為括號內部運算的最終結果,用該結果替換整個子字串,調到第1步開始重複。直到公式沒有一個運算子停止
實現程式碼:
package com.test.calc; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Stack; import java.util.function.Consumer; /**公式計算器**/ public class EquationCalculator { /** 變數集合 **/ private static List<String> varList = new LinkedList<>(); /** 變數和值的key-value **/ private static Map<String, Float> varListWithValue = new HashMap<>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("請輸入你需要計算的公式:"); String formula = scanner.nextLine().replaceAll(" ", ""); //String formula = "f=k * (((m1 + m2) * (40 * x + 60 * y)) + 100) * m2 / (G1 + G2)".replaceAll(" ", ""); fortest String equalationLeft = formula.split("=")[0]; String equalationRight = formula.split("=")[1]; /** 獲取變量表: **/ getVarList(equalationRight); /** 給變數賦值 **/ assignmentVarList(varList); /** 代回公式進行替換 **/ String newFormula = replaceEquation(formula); /** 進行計算 **/ System.out.println(); float result = calc(newFormula); System.out.println("最終結果:" + equalationLeft + "=" + result); } /** 進行計算 **/ private static float calc(String newFormula) { boolean stillHaveCalcSymbol = false; do{ //System.out.println("before:" + newFormula); /** 尋找最後一個左括號裡面到第一個右括號裡面1的內容 **/ char formulaArray[] = newFormula.toCharArray(); for (int i = 0; i < formulaArray.length; i++) { if (formulaArray[i] == '+' || formulaArray[i] == '-' || formulaArray[i] == '*' || formulaArray[i] == '/' || formulaArray[i] == '(' || formulaArray[i] == ')') { stillHaveCalcSymbol = true; } else { stillHaveCalcSymbol = false; } } if (stillHaveCalcSymbol) { String resultFormula = ""; //找最內層的括號裡面的內容出來(含括號) for (int i = 0; i < formulaArray.length; i++) { if (formulaArray[i] == ')') { int begin = 0; for (int j = i; j >= 0; j--) { if (formulaArray[j] == '(') { begin = j; break; } } String calcString = newFormula.substring(begin, i + 1); resultFormula = newFormula.replace(calcString, calcProc(calcString) + ""); //System.out.println(calcString); break; } } newFormula = resultFormula; } } while(stillHaveCalcSymbol); //最後得到普通的順序無括號公式: System.out.println(newFormula); //最後一次計算: float result = calcProc("(" + newFormula.split("=")[1] + ")"); return result; } /**詳細計算過程**/ private static float calcProc(String calcString) { // if(calcString.contains("=")){ // calcString = calcString.split("=")[1]; // } //calcString = calcString.replace("(", ""); //calcString = calcString.replace(")", ""); String calcSymbol[] = {"\\*", "\\/", "\\+", "\\-"}; char calcSymbolChar[] = {'*', '/', '+', '-'}; boolean haveSymbol = true; float result = 0f; while(haveSymbol){ System.out.println("calcStr:" + calcString); char calcCharArr[] = calcString.toCharArray(); result = 0f; for (int i = 0; i < calcSymbol.length; i++) { boolean alreadyFind = false; for(int j = 0; j < calcCharArr.length; j++){ if(calcCharArr[j] == calcSymbolChar[i]){ //System.out.println("找到了" + calcSymbolChar[i]); //以符號為中心,以左右兩邊的其他符號為邊界找到兩邊的數 float num1 = 0f; float num2 = 0f; int bottom = 0; for(int k = j - 1; k >= 0 && (calcCharArr[k] >= '0' && calcCharArr[k] <= '9' || calcCharArr[k] == '.') ; k--){ //System.out.println(calcCharArr[k] + ""); bottom = k; } //System.out.println("[j, bottom]:" + String.format("[%d, %d]", j, bottom)); num1 = Float.valueOf(calcString.substring(bottom, j)); System.out.println("num1:" + num1); int top = 0; for(int k = j + 1; k < calcString.length() && (calcCharArr[k] >= '0' && calcCharArr[k] <= '9' || calcCharArr[k] == '.'); k++){ top = k; } num2 = Float.valueOf(calcString.substring(j + 1, top + 1)); System.out.println("num2:" + num2); switch(calcSymbolChar[i]){ case '*': result = num1 * num2; break; case '/': result = num1 / num2; break; case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; } //System.out.println("bottom to top:" + calcString.substring(bottom + 1, top + 1)); calcString = calcString.replace(calcString.substring(bottom, top + 1), String.format("%.5f", result)); //System.out.println("end_calcStr:" + calcString); alreadyFind = true; break; } } if(alreadyFind) break; } haveSymbol = false; if(calcString.contains("*") || calcString.contains("/") || calcString.contains("+") || calcString.contains("-")){ haveSymbol = true; //System.out.println("找到"); } else { //System.out.println("找不到"); } } //System.out.println("result:" + result); return result; } /** 代回公式進行替換 **/ private static String replaceEquation(String formula) { String newFormula = new String(formula); for (String key : varList) { newFormula = newFormula.replaceAll(key, varListWithValue.get(key) + ""); } System.out.println(newFormula); return newFormula; } /** * 給變數賦值 * * @param varList * 變數列表 **/ private static void assignmentVarList(List<String> varList) { System.out.println("請輸入各變數的對應值"); Scanner scanner = new Scanner(System.in); for (String key : varList) { System.out.print(key + "'s value is:"); varListWithValue.put(key, scanner.nextFloat()); } /*for (String key : varList) { System.out.println("key:" + key + ", value:" + varListWithValue.get(key)); }*/ } /** * 獲取變量表 * * @param equalationRight * 輸入等式的右邊 **/ private static void getVarList(String equalationRight) { System.out.println("等式右邊:" + equalationRight); char[] formulaCharArr; formulaCharArr = equalationRight.toCharArray(); //清理所有運算子 for (int i = 0; i < formulaCharArr.length; i++) { if (formulaCharArr[i] == '+' || formulaCharArr[i] == '-' || formulaCharArr[i] == '*' || formulaCharArr[i] == '/' || formulaCharArr[i] == '(' || formulaCharArr[i] == ')') { formulaCharArr[i] = ' '; } } /*String temp = ""; for (int i = 0; i < formulaCharArr.length; i++) { if (formulaCharArr[i] == ' ') { String content = temp.trim(); if (content.length() > 0) { boolean okGo = true; if(content.charAt(0) >= '0' && content.charAt(0) <= '9'){ okGo = false; } if (okGo) { varList.add(content); } } temp = ""; } else { temp += formulaCharArr[i]; } }*/ String pa[] = new String(formulaCharArr).split(" "); for(String temp : pa){ if(temp != null && temp != "" && !temp.isEmpty()){ boolean okGo = true; if(temp.charAt(0) >= '0' && temp.charAt(0) <= '9'){ okGo = false; } if (okGo) { varList.add(temp); } } } System.out.println("變數列表:"); for (int h = 0; h < varList.size(); h++) { String var = varList.get(h); System.out.println(var); } } }
測試1:
請輸入你需要計算的公式:
j=b*y+a
等式右邊:b*y+a
變數列表:
b
y
a
請輸入各變數的對應值
b's value is:55
y's value is:66
a's value is:77
j=55.0*66.0+77.0
j=55.0*66.0+77.0
calcStr:(55.0*66.0+77.0)
num1:55.0
num2:66.0
calcStr:(3630.00000+77.0)
num1:3630.0
num2:77.0
最終結果:j=3707.0
測試2:
請輸入你需要計算的公式:
f=k * (((m1 + m2) * (40 * x + 60 * y)) + 100) * m2 / (G1 + G2) + (G3 * G4)
等式右邊:k*(((m1+m2)*(40*x+60*y))+100)*m2/(G1+G2)+(G3*G4)
變數列表:
k
m1
m2
x
y
m2
G1
G2
G3
G4
請輸入各變數的對應值
k's value is:1.1
m1's value is:2.232
m2's value is:3.333
x's value is:4.567
y's value is:31.44
m2's value is:54.22
G1's value is:5454
G2's value is:111
G3's value is:1112
G4's value is:3333
f=1.1*(((2.232+54.22)*(40*4.567+60*31.44))+100)*54.22/(5454.0+111.0)+(1112.0*3333.0)
calcStr:(2.232+54.22)
num1:2.232
num2:54.22
calcStr:(40*4.567+60*31.44)
num1:40.0
num2:4.567
calcStr:(182.67999+60*31.44)
num1:60.0
num2:31.44
calcStr:(182.67999+1886.40002)
num1:182.68
num2:1886.4
calcStr:(56.452*2069.08)
num1:56.452
num2:2069.08
calcStr:(116803.71+100)
num1:116803.71
num2:100.0
calcStr:(5454.0+111.0)
num1:5454.0
num2:111.0
calcStr:(1112.0*3333.0)
num1:1112.0
num2:3333.0
f=1.1*116903.71*54.22/5565.0+3706296.0
calcStr:(1.1*116903.71*54.22/5565.0+3706296.0)
num1:1.1
num2:116903.71
calcStr:(128594.08594*54.22/5565.0+3706296.0)
num1:128594.086
num2:54.22
calcStr:(6972371.50000/5565.0+3706296.0)
num1:6972371.5
num2:5565.0
calcStr:(1252.89697+3706296.0)
num1:1252.897
num2:3706296.0
最終結果:f=3707549.0