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
运行结果:
优化支持浮点数计算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 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); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义