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

   

posted @   落枫飘飘  阅读(11868)  评论(4编辑  收藏  举报
编辑推荐:
· .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 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示