Java实现四则运算,使用堆栈,检查语法

    突然发闲想试一试自己实现算术的四则运算,支持加减乘除和括号、正负号;支持语法检查;思路很常规,利用两个堆栈,一个压操作符,一个压操作数,念头冒出来之后,立马动手;然后本以为很容易的一个实现,却存在各种各样的坑,正常逻辑花了1个小时,填坑缺填了5个小时,不多说,上代码;

    能够检测的语法错误:缺少操作数、缺少操作符、缺失括号、不合法的数值;

    支持运算程度:全部使用浮点数float;支持任意位置的空格、制表符、回车;多重括号;

    视为语法错误的约束:空括号、多重正负号(非加减号)、除数为0;

    编码能力有限,望各路大神海涵;

  1 import java.util.HashMap;
  2 import java.util.Map;
  3 import java.util.Stack;
  4 
  5 public class FunctionStack {
  6 
  7     private Map<String, Integer> optLevel;
  8     private boolean nextIsOpt = false;
  9     private boolean debug = false;
 10     private int debug_len = 3;
 11 
 12     public FunctionStack setDebugLen(int len) {
 13         this.debug_len = len;
 14         return this;
 15     }
 16 
 17     public FunctionStack setDebug(boolean debug) {
 18         this.debug = debug;
 19         return this;
 20     }
 21 
 22     private void println(Object obj) {
 23         if (debug) {
 24             System.out.println(obj);
 25         }
 26     }
 27 
 28     public FunctionStack() {
 29         optLevel = new HashMap<String, Integer>();
 30         optLevel.put("(", 1);
 31         optLevel.put("+", 2);
 32         optLevel.put("-", 2);
 33         optLevel.put("*", 3);
 34         optLevel.put("/", 3);
 35         optLevel.put(")", 4);
 36     }
 37 
 38     public static void main(String[] args) {
 39 
 40 
 41         FunctionStack fs = new FunctionStack();
 42         fs.setDebug(true).setDebugLen(5);
 43         String fun = " 1 + 2 * 3 / 4 ";
 44         try {
 45             float res = fs.execute(fun);
 46             System.out.println("结果为:" + res);
 47         } catch (FunctionStackException e) {
 48             System.out.println(e.getMessage());
 49         }
 50         System.out.println("结束.");
 51     }
 52 
 53     /**
 54      * 
 55      * @param fun
 56      * @return
 57      * @throws FunctionStackException
 58      */
 59     public float execute(String fun) throws FunctionStackException {
 60         this.clear();
 61         // TODO Auto-generated method stub
 62         if (fun == null || fun.trim().length() == 0) {
 63             throw new FunctionStackException("表达式不能为空;");
 64         }
 65         // 创建操作符堆栈和操作数堆栈
 66         Stack<String> opt = new Stack<String>();
 67         Stack<Float> num = new Stack<Float>();
 68         // 扫描整个表达式
 69         // point记录上一个扫描点
 70         int point = 0;
 71         for (int i = 0; i < fun.length(); i++) {
 72             String scanOpt = fun.charAt(i) + "";
 73             if (scanOpt.equals("(")) {
 74                 // 发现左括号,压入栈
 75                 // 检查是否为空的括号
 76                 int right = fun.indexOf(')', i);
 77                 if (right < 0) {
 78                     // 如果没有找到右括号
 79                     throw new FunctionStackException(fun
 80                             + "
语法错误:"
 81                             + "缺少右括号与之对应:"
 82                             + fun.substring(Math.max(point - debug_len, 0),
 83                                     Math.min(i + debug_len, fun.length())));
 84                 } else {
 85                     // 找到右括号,检查是否为空括号
 86                     if (fun.substring(i + 1, right).trim().length() == 0) {
 87                         throw new FunctionStackException(fun
 88                                 + "
语法错误:"
 89                                 + "括号中内容不可为空:"
 90                                 + fun.substring(Math.max(point - debug_len, 0),
 91                                         Math.min(i + debug_len, fun.length())));
 92                     }
 93                 }
 94                 // 检查语法
 95                 String num_before_opt = fun.substring(point, i).trim();
 96                 if (num_before_opt.length() != 0) {
 97                     throw new FunctionStackException(fun
 98                             + "
语法错误:"
 99                             + "括号前缺少操作符:"
100                             + fun.substring(Math.max(point - debug_len, 0),
101                                     Math.min(i + debug_len, fun.length())));
102                 }
103                 // 将左括号压入栈
104                 println("↓压栈:" + fun.substring(i, i + 1));
105                 opt.push(fun.substring(i, i + 1));
106                 // 记录扫描点
107                 point = i + 1;
108             } else if (scanOpt.equals(")")) {
109                 // 发现右括号,取出栈,直到取出左括号
110                 println("---计算括号开始:");
111                 // 将括号前的数值取出
112                 pushFloatStack(fun, num, point, i);
113                 // 记录下一个是操作符
114                 nextIsOpt = true;
115                 // 检查前一个操作符是否为空
116                 if (opt.empty()) {
117                     throw new FunctionStackException(fun
118                             + "
语法错误:"
119                             + "缺少左括号与之对应:"
120                             + fun.substring(Math.max(point - debug_len, 0),
121                                     Math.min(i + debug_len, fun.length())));
122                 }
123                 // 取出前一个操作符
124                 String optpop = opt.pop();
125                 println("↑↑出栈:" + optpop);
126                 // 取出栈,直到取出左括号
127                 while (!optpop.equals("(")) {
128                     // 若取出的操作符不是左括号,执行运算;
129                     calculator(optpop, num);
130                     if (opt.empty()) {
131                         throw new FunctionStackException(fun
132                                 + "
语法错误:"
133                                 + "缺少左括号与之对应:"
134                                 + fun.substring(Math.max(point - debug_len, 0),
135                                         Math.min(i + debug_len, fun.length())));
136                     }
137                     optpop = opt.pop();
138                     println("↑↑出栈:" + optpop);
139                 }
140 
141                 // 记录扫描点
142                 point = i + 1;
143                 println("---计算括号结束:");
144             } else if (optLevel.get(scanOpt) != null) {
145                 if (scanOpt.equals("-") || scanOpt.equals("+")) {
146                     // 如果是减号,可能是一个负号
147                     if (fun.substring(point, i).trim().replaceAll("-", "")
148                             .replaceAll("\+", "").trim().length() == 0) {
149                         // 如果减号前没有操作数,视此减号为负号,point不移动,-也不压入堆栈
150                         continue;
151                     }
152                 }
153                 // 发现非括号的操作符,查看栈顶操作符优先级,选择计算or压栈
154                 // 获取操作数,并检查语法
155                 pushFloatStack(fun, num, point, i);
156                 // 比较优先级,将栈顶优先级高的先计算
157                 if (!opt.empty()) {
158                     // 获取栈顶操作符,不取出
159                     String optpop = opt.peek();
160                     // 取栈计算,直到栈顶操作符优先级小于scanOpt
161                     while (optLevel.get(optpop) >= optLevel.get(scanOpt)) {
162                         calculator(optpop, num);
163                         optpop = opt.pop();
164                         println("↑↑出栈:" + optpop);
165                         if (opt.empty()) {
166                             // 如果操作符取空了则退出
167                             break;
168                         }
169                         optpop = opt.peek();
170                     }
171                 }
172                 // 压入操作符
173                 println("↓压栈:" + scanOpt);
174                 opt.push(scanOpt);
175                 // 记录扫描点
176                 point = i + 1;
177             } else if (scanOpt.equals("=")) {
178                 // 发现=号,取栈计算总结果,并提前结束循环
179                 // 获取操作数,并检查语法
180                 pushFloatStack(fun, num, point, i);
181                 // 取栈计算直到结束
182                 while (!opt.empty()) {
183                     String optpop = opt.pop();
184                     println("↑↑出栈:" + optpop);
185                     calculator(optpop, num);
186                 }
187                 return getResult(num);
188             }
189 
190         }
191 
192         // 表达式结束
193         // 获取操作数,并检查语法
194         pushFloatStack(fun, num, point, fun.length());
195         // 取栈计算直到结束
196         while (!opt.empty()) {
197             String optpop = opt.pop();
198             println("↑↑出栈:" + optpop);
199             calculator(optpop, num);
200         }
201         return getResult(num);
202     }
203 
204     private void clear() {
205         // TODO Auto-generated method stub
206         nextIsOpt = false;
207     }
208 
209     private float getResult(Stack<Float> num) {
210         // TODO Auto-generated method stub
211         Float res = num.pop();
212         if (num.empty()) {
213             return res;
214         } else {
215             throw new FunctionStackException("计算错误,堆栈中还有数据;");
216         }
217     }
218 
219     /**
220      * 将操作符i前的操作数解析,并压入堆栈
221      * 
222      * @param fun
223      * @param num
224      * @param point
225      *            操作数起点
226      * @param i
227      *            操作符位置,即操作数终点
228      */
229     private void pushFloatStack(String fun, Stack<Float> num, int point, int i) {
230         String num_before_opt = fun.substring(point, i).trim();
231         if (num_before_opt.length() == 0) {
232             // 没有操作数
233             if (nextIsOpt) {
234 
235                 // 应该没有操作数,(即此处本应只有操作符,没有操作数)
236                 nextIsOpt = false;
237                 return;
238             } else {
239                 // 应该有操作数
240                 throw new FunctionStackException(fun
241                         + "
语法错误:"
242                         + "缺少操作数:"
243                         + fun.substring(Math.max(point - debug_len, 0),
244                                 Math.min(i + debug_len, fun.length())));
245             }
246         } else {
247             // 有操作数
248             if (nextIsOpt) {
249                 // 应该没有操作数
250                 throw new FunctionStackException(fun
251                         + "
语法错误:"
252                         + "缺少操作符:"
253                         + fun.substring(Math.max(point - debug_len, 0),
254                                 Math.min(i + debug_len, fun.length())));
255             } else {
256                 // 应该有操作数
257                 try {
258                     // 去除操作数中间的空格、回车、制表符
259                     num_before_opt = num_before_opt.replaceAll(" ", "");
260                     num_before_opt = num_before_opt.replaceAll("	", "");
261                     num_before_opt = num_before_opt.replaceAll("
", "");
262                     Float scannum = Float.parseFloat(num_before_opt);
263                     println("↓压栈:" + scannum);
264                     num.push(scannum);
265                 } catch (NumberFormatException e) {
266                     throw new FunctionStackException(fun + "
语法错误:"
267                             + "无法识别的数值:" + num_before_opt);
268                 }
269             }
270         }
271     }
272 
273     /**
274      * 
275      * @param optpop
276      *            运算符
277      */
278     private void calculator(String optpop, Stack<Float> num) {
279         // TODO Auto-generated method stub
280         Float pop2 = num.pop();
281         println("↑↑出栈:" + pop2);
282         Float pop1 = num.pop();
283         println("↑↑出栈:" + pop1);
284         println("--------计算 " + pop1 + optpop + pop2);
285         if (optpop.equals("+")) {
286             println("↓压栈:" + (pop1 + pop2));
287             num.push(pop1 + pop2);
288         } else if (optpop.equals("-")) {
289             println("↓压栈:" + (pop1 - pop2));
290             num.push(pop1 - pop2);
291         } else if (optpop.equals("*")) {
292             println("↓压栈:" + (pop1 * pop2));
293             num.push(pop1 * pop2);
294         } else if (optpop.equals("/")) {
295             if (pop2 == 0) {
296                 throw new FunctionStackException("语法错误:" + "除数不可以为零:" + pop2);
297             }
298             println("↓压栈:" + (pop1 / pop2));
299             num.push(pop1 / pop2);
300         } else if (optpop.equals("(")) {
301             throw new FunctionStackException("语法错误:" + "缺少右括号与之对应:" + optpop);
302         } else {
303             throw new FunctionStackException("语法错误:" + "错误的操作符:" + optpop);
304         }
305 
306     }
307 
308 }
主代码
 1 public class FunctionStackException extends RuntimeException {
 2 
 3     /**
 4      * 
 5      */
 6     private static final long serialVersionUID = 1L;
 7 
 8     public FunctionStackException(String message) {
 9         super(message);
10     }
11 
12 }
自定义异常
原文地址:https://www.cnblogs.com/luoyesailing-qq-77149514/p/5773631.html