这个计算器不仅能够进行四则运算,还支持添加括号进行优先级计算,例如下面算式:
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); } }