这个计算器不仅能够进行四则运算,还支持添加括号进行优先级计算,例如下面算式:

  1. 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. }

运行结果:

优化支持浮点数计算:

  1. import java.math.BigDecimal;
  2. import java.util.Stack;
  3.  
  4. /**
  5. * 算式计算
  6. */
  7. public class FormulaUtil {
  8.  
  9. private int scale; // 进行除法出现无线循环小数时保留的精度
  10.  
  11. /** 数字栈:用于存储表达式中的各个数字 */
  12. private Stack<BigDecimal> numberStack = null;
  13. /** 符号栈:用于存储运算符和括号 */
  14. private Stack<Character> symbolStack = null;
  15.  
  16. public FormulaUtil(int scale) {
  17. super();
  18. this.scale = scale;
  19. }
  20.  
  21. public FormulaUtil() {
  22. this(32);
  23. }
  24.  
  25. /**
  26. * 解析并计算四则运算表达式(含括号优先级),返回计算结果
  27. *
  28. * @param numStr
  29. * 算术表达式(含括号)
  30. */
  31. public BigDecimal caculate(String numStr) {
  32. numStr = removeStrSpace(numStr); // 去除空格
  33. // 如果算术表达式尾部没有‘=’号,则在尾部添加‘=’,表示结束符
  34. if (numStr.length() > 1
  35. && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
  36. numStr += "=";
  37. }
  38. // 检查表达式是否合法
  39. if (!isStandard(numStr)) {
  40. System.err.println("错误:算术表达式有误!");
  41. return null;
  42. }
  43. // 初始化栈
  44. if (numberStack == null) {
  45. numberStack = new Stack<BigDecimal>();
  46. }
  47. numberStack.clear();
  48. if (symbolStack == null) {
  49. symbolStack = new Stack<Character>();
  50. }
  51. symbolStack.clear();
  52. // 用于缓存数字,因为数字可能是多位的
  53. StringBuffer temp = new StringBuffer();
  54. // 从表达式的第一个字符开始处理
  55. for (int i = 0; i < numStr.length(); i++) {
  56. char ch = numStr.charAt(i); // 获取一个字符
  57. if (isNumber(ch)) { // 若当前字符是数字
  58. temp.append(ch); // 加入到数字缓存中
  59. } else { // 非数字的情况
  60. String tempStr = temp.toString(); // 将数字缓存转为字符串
  61. if (!tempStr.isEmpty()) {
  62. // long num = Long.parseLong(tempStr); // 将数字字符串转为长整型数
  63. BigDecimal num = new BigDecimal(tempStr);
  64. numberStack.push(num); // 将数字压栈
  65. temp = new StringBuffer(); // 重置数字缓存
  66. }
  67. // 判断运算符的优先级,若当前优先级低于栈顶的优先级,则先把计算前面计算出来
  68. while (!comparePri(ch) && !symbolStack.empty()) {
  69. BigDecimal b = numberStack.pop(); // 出栈,取出数字,后进先出
  70. BigDecimal a = numberStack.pop();
  71. // 取出运算符进行相应运算,并把结果压栈进行下一次运算
  72. switch ((char) symbolStack.pop()) {
  73. case '+':
  74. numberStack.push(a.add(b));
  75. break;
  76. case '-':
  77. numberStack.push(a.subtract(b));
  78. break;
  79. case '*':
  80. numberStack.push(a.multiply(b));
  81. break;
  82. case '/':
  83. try {
  84. numberStack.push(a.divide(b));
  85. } catch (java.lang.ArithmeticException e) {
  86. // 进行除法出现无限循环小数时,就会抛异常,此处设置精度重新计算
  87. numberStack.push(a.divide(b, this.scale,
  88. BigDecimal.ROUND_HALF_EVEN));
  89. }
  90. break;
  91. default:
  92. break;
  93. }
  94. } // while循环结束
  95. if (ch != '=') {
  96. symbolStack.push(new Character(ch)); // 符号入栈
  97. if (ch == ')') { // 去括号
  98. symbolStack.pop();
  99. symbolStack.pop();
  100. }
  101. }
  102. }
  103. } // for循环结束
  104.  
  105. return numberStack.pop(); // 返回计算结果
  106. }
  107.  
  108. /**
  109. * 去除字符串中的所有空格
  110. */
  111. private String removeStrSpace(String str) {
  112. return str != null ? str.replaceAll(" ", "") : "";
  113. }
  114.  
  115. /**
  116. * 检查算术表达式的基本合法性,符合返回true,否则false
  117. */
  118. private boolean isStandard(String numStr) {
  119. if (numStr == null || numStr.isEmpty()) // 表达式不能为空
  120. return false;
  121. Stack<Character> stack = new Stack<Character>(); // 用来保存括号,检查左右括号是否匹配
  122. boolean b = false; // 用来标记'='符号是否存在多个
  123. for (int i = 0; i < numStr.length(); i++) {
  124. char n = numStr.charAt(i);
  125. // 判断字符是否合法
  126. if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
  127. || "+".equals(n + "") || "-".equals(n + "")
  128. || "*".equals(n + "") || "/".equals(n + "") || "=".equals(n
  129. + ""))) {
  130. return false;
  131. }
  132. // 将左括号压栈,用来给后面的右括号进行匹配
  133. if ("(".equals(n + "")) {
  134. stack.push(n);
  135. }
  136. if (")".equals(n + "")) { // 匹配括号
  137. if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) // 括号是否匹配
  138. return false;
  139. }
  140. // 检查是否有多个'='号
  141. if ("=".equals(n + "")) {
  142. if (b)
  143. return false;
  144. b = true;
  145. }
  146. }
  147. // 可能会有缺少右括号的情况
  148. if (!stack.isEmpty())
  149. return false;
  150. // 检查'='号是否不在末尾
  151. if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
  152. return false;
  153. return true;
  154. }
  155.  
  156. /**
  157. * 判断字符是否是0-9的数字
  158. */
  159. private boolean isNumber(char num) {
  160. if ((num >= '0' && num <= '9') || num == '.')
  161. return true;
  162. return false;
  163. }
  164.  
  165. /**
  166. * 比较优先级:如果当前运算符比栈顶元素运算符优先级高则返回true,否则返回false
  167. */
  168. private boolean comparePri(char symbol) {
  169. if (symbolStack.empty()) { // 空栈返回ture
  170. return true;
  171. }
  172.  
  173. // 符号优先级说明(从高到低):
  174. // 第1级: (
  175. // 第2级: * /
  176. // 第3级: + -
  177. // 第4级: )
  178.  
  179. char top = (char) symbolStack.peek(); // 查看堆栈顶部的对象,注意不是出栈
  180. if (top == '(') {
  181. return true;
  182. }
  183. // 比较优先级
  184. switch (symbol) {
  185. case '(': // 优先级最高
  186. return true;
  187. case '*': {
  188. if (top == '+' || top == '-') // 优先级比+和-高
  189. return true;
  190. else
  191. return false;
  192. }
  193. case '/': {
  194. if (top == '+' || top == '-') // 优先级比+和-高
  195. return true;
  196. else
  197. return false;
  198. }
  199. case '+':
  200. return false;
  201. case '-':
  202. return false;
  203. case ')': // 优先级最低
  204. return false;
  205. case '=': // 结束符
  206. return false;
  207. default:
  208. break;
  209. }
  210. return true;
  211. }
  212.  
  213. // 测试
  214. public static void main(String args[]) {
  215. String numStr = "10.000000000000000009 + (2*16-20/4) + 7*2.5 "; // 默认的算式
  216. BigDecimal result = new FormulaUtil().caculate(numStr); // 计算算式的结果
  217. System.out.println(numStr + "=");
  218. System.out.println(result);
  219. }
  220. }

  

Java实现带括号优先级的计算器的更多相关文章

  1. java实现可有括号的android计算器

    写了一个android版的计算器,可以计算带括号的表达式,不过前提是:正确的表达式才行 小缺陷是没有做表达式括号的控制,现在还没有想到好的控制方式 package javaAdvanced; impo ...

  2. Python带括号的计算器

    带括号的计算器也是第一个自我感觉完成最好的 毕竟真的弄了一个多星期 虽然前期这路真的很难走  我会努力加油  将Python学好学踏实 参考了两位博主的文章 http://www.cnblogs.co ...

  3. 使用python开发一个能够计算带括号的复杂表达式的计算器(只支持加减乘除)

    使用到了模块re,正则,字典等 # 实现简单的加减乘除括号等运算 # Calculator def calculator(expression): print(expression) import r ...

  4. java中运算符的优先级

    所谓优先级,就是在表达式中的运算顺序.Java 中常用的运算符的优先级如下表所示: 级别为 1 的优先级最高,级别 11 的优先级最低.譬如,x = 7 + 3 * 2  得到的结果是 13 &quo ...

  5. 慕课网-安卓工程师初养成-3-9 Java中运算符的优先级

    来源 http://www.imooc.com/code/1315 所谓优先级,就是在表达式中的运算顺序.Java 中常用的运算符的优先级如下表所示: 级别为 1 的优先级最高,级别 11 的优先级最 ...

  6. (转载)Java自带的GUI性能监控工具Jconsole以及JisualVM简介

    原文链接:http://blog.csdn.net/chendc201/article/details/22905503 1 Jconsole 1.1 简介以及连接 JConsole是一个基于JMX的 ...

  7. Java课程设计----仿Windows标准型计算器

    JAVA课程设计 仿Windows标准型计算器(By Yanboooooooo) 一.团队介绍: 连燕波[组长]:网络1513学生. 张文博[组员]:网络1513学生. 二.项目git地址 码云项目地 ...

  8. js中new函数后带括号和不带括号的区别

    用new创建构造函数的实例时,通常情况下new 的构造函数后面需要带括号(譬如:new Parent()). 有些情况下new的构造函数后带括号和不带括号的情况一致,譬如: function Pare ...

  9. 解决VS Code开发Python3语言自动补全功能不带括号的问题

    Visual Studio Code(以下简称VS Code)用来开发Python3,还是很便利的,本身这个IDE就是轻量级的,才几十兆大小,通过安装插件的方式支持各种语言的开发.界面也美美哒,可以在 ...

随机推荐

  1. WordPress文章浏览历史插件

    选自:http://www.ludou.org/wordpress-recently-viewed.html 最近有很多网友问我,露兜博客右边栏底部的 您刚刚看过 栏目是怎么实现.其实我也是参考的这篇 ...

  2. 剑指Offer 栈的压入、弹出序列

    题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序 ...

  3. BZOJ 2466: [中山市选2009]树

    Sol 树形DP. 听说有非常神奇的高斯消元的做法...orz... 然而我只会 \(O(n)\) 的树形DP. 首先一个点的状态只于他的父节点和子树有关,跟他 子树的子树 和 父亲的父亲 都没有任何 ...

  4. [翻译]opengl扩展教程2

    [翻译]opengl扩展教程2 原文地址https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/extensions_part2.php [ ...

  5. Python自动化之IO多路复用

    单线程.多线程和异步对比图 灰色的是阻塞 IO多路复用 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心 ...

  6. Docker个人学习总结

    最近一段时间学习了下Docker,关于Docker的安装,运用什么的在这里我不会过多的说明,我只说我对于Docker的一些理解,附加我用于记录的少量代码. 刚开始学习Docker的时候,找资料在网上看 ...

  7. Jquery ajax调用webservice总结

    jquery ajax调用webservice(C#)要注意的几个事项: 1.web.config里需要配置2个地方 <httpHandlers>      <remove verb ...

  8. Linq查询非泛型集合要指定Student类型(比如List)

    #region Linq to 集合查询非泛型集合要指定Student类型            //ArrayList list = new ArrayList();            //li ...

  9. Unity3d 枚举某个目录下所有资源

    using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; us ...

  10. Xcode 必备插件管理器 http://alcatraz.io

    各种小插件,其中写注释用的 VVDocumenter 是必备的!