• 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 }
    自定义异常
  • 相关阅读:
    子查询
    主键、外键
    语句、聚合函数、数学函数、字符串函数、时间日期函数
    数据库的备份、还原、分离、附加
    SQL server数据类型、增删改查
    轮播特效
    手风琴特效
    关于Winform中的用户代理
    详细的SQL中datediff用法
    sql server 的datediff函数
  • 原文地址:https://www.cnblogs.com/luoyesailing-qq-77149514/p/5773631.html
Copyright © 2020-2023  润新知