突然发闲想试一试自己实现算术的四则运算,支持加减乘除和括号、正负号;支持语法检查;思路很常规,利用两个堆栈,一个压操作符,一个压操作数,念头冒出来之后,立马动手;然后本以为很容易的一个实现,却存在各种各样的坑,正常逻辑花了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 }