Java實現帶括號優先級的計算器


這個計算器不僅能夠進行四則運算,還支持添加括號進行優先級計算,例如下面算式:

10+(2*16-20/5)+7*2=52

 

Java源代碼:

  1 import java.awt.BorderLayout;
  2 import java.awt.Container;
  3 import java.awt.event.ActionEvent;
  4 import java.awt.event.ActionListener;
  5 import java.util.Stack;
  6 
  7 import javax.swing.JButton;
  8 import javax.swing.JFrame;
  9 import javax.swing.JLabel;
 10 import javax.swing.JPanel;
 11 import javax.swing.JTextField;
 12 
 13 /**
 14  * 計算器
 15  */
 16 public class Calculator {
 17 
 18     /** 數字棧:用於存儲表達式中的各個數字 */
 19     private Stack<Long> numberStack = null;
 20     /** 符號棧:用於存儲運算符和括號 */
 21     private Stack<Character> symbolStack = null;
 22 
 23     /**
 24      * 解析並計算四則運算表達式(含括號),返回計算結果
 25      * 
 26      * @param numStr
 27      *            算術表達式(含括號)
 28      */
 29     public long caculate(String numStr) {
 30         numStr = removeStrSpace(numStr); // 去除空格
 31         // 如果算術表達式尾部沒有‘=’號,則在尾部添加‘=’,表示結束符
 32         if (numStr.length() > 1 && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
 33             numStr += "=";
 34         }
 35         // 檢查表達式是否合法
 36         if (!isStandard(numStr)) {
 37             System.err.println("錯誤:算術表達式有誤!");
 38             return 0;
 39         }
 40         // 初始化棧
 41         numberStack = new Stack<Long>();
 42         symbolStack = new Stack<Character>();
 43         // 用於緩存數字,因為數字可能是多位的
 44         StringBuffer temp = new StringBuffer();
 45         // 從表達式的第一個字符開始處理
 46         for (int i = 0; i < numStr.length(); i++) {
 47             char ch = numStr.charAt(i); // 獲取一個字符
 48             if (isNumber(ch)) { // 若當前字符是數字
 49                 temp.append(ch); // 加入到數字緩存中
 50             } else { // 非數字的情況
 51                 String tempStr = temp.toString(); // 將數字緩存轉為字符串
 52                 if (!tempStr.isEmpty()) {
 53                     long num = Long.parseLong(tempStr); // 將數字字符串轉為長整型數
 54                     numberStack.push(num); // 將數字壓棧
 55                     temp = new StringBuffer(); // 重置數字緩存
 56                 }
 57                 // 判斷運算符的優先級,若當前優先級低於棧頂的優先級,則先把計算前面計算出來
 58                 while (!comparePri(ch) && !symbolStack.empty()) {
 59                     long b = numberStack.pop(); // 出棧,取出數字,后進先出
 60                     long a = numberStack.pop();
 61                     // 取出運算符進行相應運算,並把結果壓棧進行下一次運算
 62                     switch ((char) symbolStack.pop()) {
 63                     case '+':
 64                         numberStack.push(a + b);
 65                         break;
 66                     case '-':
 67                         numberStack.push(a - b);
 68                         break;
 69                     case '*':
 70                         numberStack.push(a * b);
 71                         break;
 72                     case '/':
 73                         numberStack.push(a / b);
 74                         break;
 75                     default:
 76                         break;
 77                     }
 78                 } // while循環結束
 79                 if (ch != '=') {
 80                     symbolStack.push(new Character(ch)); // 符號入棧
 81                     if (ch == ')') { // 去括號
 82                         symbolStack.pop();
 83                         symbolStack.pop();
 84                     }
 85                 }
 86             }
 87         } // for循環結束
 88 
 89         return numberStack.pop(); // 返回計算結果
 90     }
 91 
 92     /**
 93      * 去除字符串中的所有空格
 94      */
 95     private String removeStrSpace(String str) {
 96         return str != null ? str.replaceAll(" ", "") : "";
 97     }
 98 
 99     /**
100      * 檢查算術表達式的基本合法性,符合返回true,否則false
101      */
102     private boolean isStandard(String numStr) {
103         if (numStr == null || numStr.isEmpty()) // 表達式不能為空
104             return false;
105         Stack<Character> stack = new Stack<Character>(); // 用來保存括號,檢查左右括號是否匹配
106         boolean b = false; // 用來標記'='符號是否存在多個
107         for (int i = 0; i < numStr.length(); i++) {
108             char n = numStr.charAt(i);
109             // 判斷字符是否合法
110             if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
111                     || "+".equals(n + "") || "-".equals(n + "")
112                     || "*".equals(n + "") || "/".equals(n + "")
113                     || "=".equals(n + ""))) {
114                 return false;
115             }
116             // 將左括號壓棧,用來給后面的右括號進行匹配
117             if ("(".equals(n + "")) {
118                 stack.push(n);
119             }
120             if (")".equals(n + "")) { // 匹配括號
121                 if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) // 括號是否匹配
122                     return false;
123             }
124             // 檢查是否有多個'='號
125             if ("=".equals(n + "")) {
126                 if (b)
127                     return false;
128                 b = true;
129             }
130         }
131         // 可能會有缺少右括號的情況
132         if (!stack.isEmpty())
133             return false;
134         // 檢查'='號是否不在末尾
135         if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
136             return false;
137         return true;
138     }
139 
140     /**
141      * 判斷字符是否是0-9的數字
142      */
143     private boolean isNumber(char num) {
144         if (num >= '0' && num <= '9')
145             return true;
146         return false;
147     }
148 
149     /**
150      * 比較優先級:如果當前運算符比棧頂元素運算符優先級高則返回true,否則返回false
151      */
152     private boolean comparePri(char symbol) {
153         if (symbolStack.empty()) { // 空棧返回ture
154             return true;
155         }
156 
157         // 符號優先級說明(從高到低):
158         // 第1級: (
159         // 第2級: * /
160         // 第3級: + -
161         // 第4級: )
162 
163         char top = (char) symbolStack.peek(); // 查看堆棧頂部的對象,注意不是出棧
164         if (top == '(') {
165             return true;
166         }
167         // 比較優先級
168         switch (symbol) { 
169         case '(': // 優先級最高
170             return true;
171         case '*': {
172             if (top == '+' || top == '-') // 優先級比+和-高
173                 return true;
174             else
175                 return false;
176         }
177         case '/': {
178             if (top == '+' || top == '-') // 優先級比+和-高
179                 return true;
180             else
181                 return false;
182         }
183         case '+':
184             return false;
185         case '-':
186             return false;
187         case ')': // 優先級最低
188             return false;
189         case '=': // 結束符
190             return false;
191         default:
192             break;
193         }
194         return true;
195     }
196 
197     // 測試
198     public static void main(String args[]) {
199         String num = "10 + (2*16-20/5) + 7*2 "; // 默認的算式
200         // 創建一個窗口
201         JFrame win = new JFrame("計算器");
202         Container con = win.getContentPane();
203         JPanel pa = new JPanel();
204         pa.add(new JLabel("輸入算式:")); // 添加一個標簽
205         final JTextField formulaText = new JTextField(num, 20); // 算式輸入框
206         pa.add(formulaText);
207         pa.add(new JLabel("="));
208         final JTextField resultText = new JTextField(8); // 結果文本框
209         pa.add(resultText);
210         con.add(pa);
211 
212         JButton bn = new JButton("計算"); // 實例化按鈕對象
213         con.add(bn, BorderLayout.EAST); // 將按鈕添加到右邊
214         win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 設置關閉窗口退出程序
215         win.pack(); // 自動調整大小
216         win.setLocationRelativeTo(null); // 設置窗口居中於屏幕
217         win.setVisible(true); // 顯示窗口
218 
219         // 添加按鈕點擊事件
220         bn.addActionListener(new ActionListener() {
221             @Override
222             public void actionPerformed(ActionEvent e) { // 每當按鈕點擊時調用該方法
223                 /* 計算器操作 */
224                 Calculator cal = new Calculator();
225                 String numStr = formulaText.getText(); // 獲得算式文本框中的文字
226                 long result = cal.caculate(numStr); // 計算算式的結果
227                 numStr = cal.removeStrSpace(numStr); // 去空格
228                 formulaText.setText(numStr); // 將去空格的算式放回算式文本框中
229                 resultText.setText(result + ""); // 在結果文本框中顯示結果
230             }
231         });
232     }
233 }
234  

 

運行結果:

 

 

 

 

優化支持浮點數計算:

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

/**
 * 算式計算
 */
public class FormulaUtil {

	private int scale; // 進行除法出現無線循環小數時保留的精度

	/** 數字棧:用於存儲表達式中的各個數字 */
	private Stack<BigDecimal> numberStack = null;
	/** 符號棧:用於存儲運算符和括號 */
	private Stack<Character> symbolStack = null;

	public FormulaUtil(int scale) {
		super();
		this.scale = scale;
	}

	public FormulaUtil() {
		this(32);
	}

	/**
	 * 解析並計算四則運算表達式(含括號優先級),返回計算結果
	 * 
	 * @param numStr
	 *            算術表達式(含括號)
	 */
	public BigDecimal caculate(String numStr) {
		numStr = removeStrSpace(numStr); // 去除空格
		// 如果算術表達式尾部沒有‘=’號,則在尾部添加‘=’,表示結束符
		if (numStr.length() > 1
				&& !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
			numStr += "=";
		}
		// 檢查表達式是否合法
		if (!isStandard(numStr)) {
			System.err.println("錯誤:算術表達式有誤!");
			return null;
		}
		// 初始化棧
		if (numberStack == null) {
			numberStack = new Stack<BigDecimal>();
		}
		numberStack.clear();
		if (symbolStack == null) {
			symbolStack = new Stack<Character>();
		}
		symbolStack.clear();
		// 用於緩存數字,因為數字可能是多位的
		StringBuffer temp = new StringBuffer();
		// 從表達式的第一個字符開始處理
		for (int i = 0; i < numStr.length(); i++) {
			char ch = numStr.charAt(i); // 獲取一個字符
			if (isNumber(ch)) { // 若當前字符是數字
				temp.append(ch); // 加入到數字緩存中
			} else { // 非數字的情況
				String tempStr = temp.toString(); // 將數字緩存轉為字符串
				if (!tempStr.isEmpty()) {
					// long num = Long.parseLong(tempStr); // 將數字字符串轉為長整型數
					BigDecimal num = new BigDecimal(tempStr);
					numberStack.push(num); // 將數字壓棧
					temp = new StringBuffer(); // 重置數字緩存
				}
				// 判斷運算符的優先級,若當前優先級低於棧頂的優先級,則先把計算前面計算出來
				while (!comparePri(ch) && !symbolStack.empty()) {
					BigDecimal b = numberStack.pop(); // 出棧,取出數字,后進先出
					BigDecimal a = numberStack.pop();
					// 取出運算符進行相應運算,並把結果壓棧進行下一次運算
					switch ((char) symbolStack.pop()) {
					case '+':
						numberStack.push(a.add(b));
						break;
					case '-':
						numberStack.push(a.subtract(b));
						break;
					case '*':
						numberStack.push(a.multiply(b));
						break;
					case '/':
						try {
							numberStack.push(a.divide(b));
						} catch (java.lang.ArithmeticException e) {
							// 進行除法出現無限循環小數時,就會拋異常,此處設置精度重新計算
							numberStack.push(a.divide(b, this.scale,
									BigDecimal.ROUND_HALF_EVEN));
						}
						break;
					default:
						break;
					}
				} // while循環結束
				if (ch != '=') {
					symbolStack.push(new Character(ch)); // 符號入棧
					if (ch == ')') { // 去括號
						symbolStack.pop();
						symbolStack.pop();
					}
				}
			}
		} // for循環結束

		return numberStack.pop(); // 返回計算結果
	}

	/**
	 * 去除字符串中的所有空格
	 */
	private String removeStrSpace(String str) {
		return str != null ? str.replaceAll(" ", "") : "";
	}

	/**
	 * 檢查算術表達式的基本合法性,符合返回true,否則false
	 */
	private boolean isStandard(String numStr) {
		if (numStr == null || numStr.isEmpty()) // 表達式不能為空
			return false;
		Stack<Character> stack = new Stack<Character>(); // 用來保存括號,檢查左右括號是否匹配
		boolean b = false; // 用來標記'='符號是否存在多個
		for (int i = 0; i < numStr.length(); i++) {
			char n = numStr.charAt(i);
			// 判斷字符是否合法
			if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
					|| "+".equals(n + "") || "-".equals(n + "")
					|| "*".equals(n + "") || "/".equals(n + "") || "=".equals(n
					+ ""))) {
				return false;
			}
			// 將左括號壓棧,用來給后面的右括號進行匹配
			if ("(".equals(n + "")) {
				stack.push(n);
			}
			if (")".equals(n + "")) { // 匹配括號
				if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) // 括號是否匹配
					return false;
			}
			// 檢查是否有多個'='號
			if ("=".equals(n + "")) {
				if (b)
					return false;
				b = true;
			}
		}
		// 可能會有缺少右括號的情況
		if (!stack.isEmpty())
			return false;
		// 檢查'='號是否不在末尾
		if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
			return false;
		return true;
	}

	/**
	 * 判斷字符是否是0-9的數字
	 */
	private boolean isNumber(char num) {
		if ((num >= '0' && num <= '9') || num == '.')
			return true;
		return false;
	}

	/**
	 * 比較優先級:如果當前運算符比棧頂元素運算符優先級高則返回true,否則返回false
	 */
	private boolean comparePri(char symbol) {
		if (symbolStack.empty()) { // 空棧返回ture
			return true;
		}

		// 符號優先級說明(從高到低):
		// 第1級: (
		// 第2級: * /
		// 第3級: + -
		// 第4級: )

		char top = (char) symbolStack.peek(); // 查看堆棧頂部的對象,注意不是出棧
		if (top == '(') {
			return true;
		}
		// 比較優先級
		switch (symbol) {
		case '(': // 優先級最高
			return true;
		case '*': {
			if (top == '+' || top == '-') // 優先級比+和-高
				return true;
			else
				return false;
		}
		case '/': {
			if (top == '+' || top == '-') // 優先級比+和-高
				return true;
			else
				return false;
		}
		case '+':
			return false;
		case '-':
			return false;
		case ')': // 優先級最低
			return false;
		case '=': // 結束符
			return false;
		default:
			break;
		}
		return true;
	}

	// 測試
	public static void main(String args[]) {
		String numStr = "10.000000000000000009 + (2*16-20/4) + 7*2.5 "; // 默認的算式
		BigDecimal result = new FormulaUtil().caculate(numStr); // 計算算式的結果
		System.out.println(numStr + "=");
		System.out.println(result);
	}
}

   


免責聲明!

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



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