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);
	}
}

   

posted @ 2016-07-14 20:37  落枫飘飘  阅读(11621)  评论(4编辑  收藏  举报