• java实现计算复杂逻辑表达式


    最近遇到一个需求,需要对逻辑表达式进行计算,支持数据类型包括数字,日期以及字符串,运算符包括<,<=,>,>=,(,),==,!=,&&,||。

    代码结构:

      OperatorEnum 运算符枚举类 枚举了支持的运算符,信息包括运算符及他们的优先级

      OperandTypeEnum 数据类型枚举类

      LogicUtil 逻辑运算工具类,实现逻辑运算

      DateUtil 日期工具类,提供验证是否为日期以及比较大小方法,是否为日期类型见https://www.cnblogs.com/lin0/p/13323925.html

    (后续还在实现逻辑表达式中添加了判断表达式是否合法代码,括号不匹配,运算符两边数据类型不一致,逻辑表达式不完整等,未粘贴上来)

    OperatorEnum:

    import java.util.HashMap;
    import java.util.Map;
    
    public enum OperatorEnum {
        //!,&,|运算时暂时未考虑在内,主要用于判断字符是否为操作
        NOT("!",0),
        LT("<",1),
        ELT("<=",1),
        GT(">",1),
        EGT(">=",1),
        EQ("==",2),
        NEQ("!=",2),
        BAND("&", 3),
        BOR("|", 4),
        AND("&&",5),
        OR("||",6),
        E("=", 7);
        private String name;
        private Integer priority;
        OperatorEnum(String name, Integer priority){
            this.name = name;
            this.priority = priority;
        }
    
        private static Map<String, OperatorEnum> enums = new HashMap<>();
        public static void enumToMap(){
            enums.put("!", OperatorEnum.NOT);
            enums.put("<", OperatorEnum.LT);
            enums.put("<=", OperatorEnum.ELT);
            enums.put(">", OperatorEnum.GT);
            enums.put(">=", OperatorEnum.EGT);
            enums.put("==", OperatorEnum.EQ);
            enums.put("!=", OperatorEnum.NEQ);
            enums.put("&", OperatorEnum.BAND);
            enums.put("|", OperatorEnum.BOR);
            enums.put("&&", OperatorEnum.AND);
            enums.put("||", OperatorEnum.OR);
            enums.put("=", OperatorEnum.E);
        }
    
        public static OperatorEnum getEnumByName(String name){
            if(enums.size() < 1){
                enumToMap();
            }
            return enums.get(name);
        }
    
        public static boolean isOperator(String name){
            if(enums.size() < 1){
                enumToMap();
            }
            return enums.containsKey(name);
        }
    
        public Integer getPriority() {
            return priority;
        }
    
        public String getName() {
            return name;
        }
    
    }

    OperandTypeEnum:

    public enum OperandTypeEnum {
        NUM("数字"),
        DATE("日期"),
        STR("字符串");
        private String typeName;
        OperandTypeEnum(String typeName){
            this.typeName = typeName;
        }
    }

    DateUtil:

    /**
         * @author Carol
         * 比较日期大小
         * @return
         */
        public static int compareDate(String date1, String date2){
            String d1 = date1.trim().replaceAll("-|:|/| ","");
            String d2 = date2.trim().replaceAll("-|:|/| ", "");
            StringBuilder sb1 = new StringBuilder(d1);
            StringBuilder sb2 = new StringBuilder(d2);
            while(sb1.length() < 14){
                sb1.append("0");
            }
            while(sb2.length() < 14){
                sb2.append("0");
            }
            long num1 = Long.parseLong(sb1.toString());
            long num2 = Long.parseLong(sb2.toString());
            return num1 == num2 ? 0 : num1 > num2 ? 1 : -1;
        }

    LogicUtil:

    /**
         * @author Carol
         * 计算表达式
         * 利用一个操作数栈和一个操作栈来进行计算
         * 出栈情况:当前字符串为操作并且优先级小于栈顶操作元素或者遇到“)”,将操作栈中栈顶元素出栈,操作数栈中出栈俩元素
         * 入栈情况:“(”直接入栈,或者当前操作优先级大于栈顶元素则直接入栈,操作数直接入栈
         * @param expression
         * @return
         */
        public static boolean parse2(String expression) {
            Deque<String> operands = new LinkedList<>();//操作数栈
            Deque<String> operate = new LinkedList<>();//操作栈
            StringBuilder sb = new StringBuilder();
            try {
                byte[] bytes = expression.getBytes("GBK");
                for (int i = 0 ; i < bytes.length ; i ++) {
                    //汉字
                    if (bytes[i] < 0) {
                        if (i == bytes.length - 1) {//字符串异常截断
                            //不要这个字
                        } else {
                            sb.append(new String(new byte[]{bytes[i], bytes[i + 1]}, "GBK"));
                            i ++;
                        }
                        continue;
                    }
                    String thisOp = new String(new byte[]{bytes[i]});
                    //直接入栈
                    if ("(".equals(thisOp)) {
                        pushOperandIntoStack(operands, sb);
                        operate.addFirst(thisOp);
                        continue;
                    }
                    //到“(”之前的全部出栈
                    if (")".equals(thisOp)) {
                        pushOperandIntoStack(operands, sb);
                        String topOp = operate.pollFirst();
                        while(!"(".equals(topOp)){
                            calculate(operands, topOp);
                            if (operate.size() > 0) {
                                topOp = operate.pollFirst();
                            } else {
                                //括号没匹配上,逻辑表达式出错
                                return false;
                            }
    
                        }
                        continue;
                    }
                    if (OperatorEnum.isOperator(thisOp)) {//当前是否为操作符
                        //是,1.查看之前是否有字符未入栈,先入栈字符
                        pushOperandIntoStack(operands, sb);
                        //2.查看下一个是否为操作,并且非括号,是合并当前一起入操作栈
                        String nextOp = new String(new byte[]{bytes[i + 1]});
                        //下个与当前一起组成一个操作
                        if (!"(".equals(nextOp) && !")".equals(nextOp) && OperatorEnum.isOperator(nextOp)) {
                            thisOp += nextOp;
                            i ++;
                        }
                        //判断当前操作与栈顶操作优先级
                        if(operate.size() > 0){
                            String topOp = operate.getFirst();
                            while(!"(".equals(topOp) && OperatorEnum.getEnumByName(topOp).getPriority() < OperatorEnum.getEnumByName(thisOp).getPriority()){
                                //优先级高,出栈进行计算
                                topOp = operate.pollFirst();
                                calculate(operands, topOp);
                                if (operate.size() > 0) {
                                    topOp = operate.getFirst();
                                } else {
                                    break;
                                }
    
                            }
                        }
                        operate.addFirst(thisOp);
                    } else {
                        sb.append(thisOp);
                    }
                }
            } catch (Exception e){
                e.printStackTrace();
            }
            if(sb.length() > 0){
                operands.addFirst(sb.toString());
            }
            while(operate.size() > 0){
                String topOp = operate.pollFirst();
                calculate(operands, topOp);
            }
            if (operands.size() > 0){
                String str = operands.pollFirst();
                return StringUtils.isNotBlank(str) ? Boolean.parseBoolean(str) : false;
            }
            return false;
        }
    
        private static void pushOperandIntoStack(Deque operands, StringBuilder sb){
            if(sb.length() > 0){
                operands.addFirst(sb.toString());
                sb.setLength(0);
            }
        }
    
        private static void calculate(Deque<String> operands, String topOp){
            String operand2 = operands.pollFirst().trim();
            String operand1 = operands.pollFirst().trim();
            //判断两个操作数类型,不一致不可比较直接返回false
            OperandTypeEnum type1 = judgeType(operand1);
            OperandTypeEnum type2 = judgeType(operand2);
            if (type1 == type2) {
                switch (type1){
                    case NUM:
                        operands.addFirst(numCalculate(Long.parseLong(operand1), Long.parseLong(operand2),
                                OperatorEnum.getEnumByName(topOp)) + "");
                        break;
                    case DATE:
                        operands.addFirst(dateCalculate(operand1, operand2, OperatorEnum.getEnumByName(topOp)) + "");
                        break;
                    case STR:
                        operands.addFirst(strCalculate(operand1, operand2, OperatorEnum.getEnumByName(topOp)) + "");
                        break;
                }
            } else {
                operands.addFirst("false");
            }
        }
    
        private static OperandTypeEnum judgeType (String operands) {
            operands = operands.trim();
            if (Pattern.matches("^[-\+]?[\d]*$", operands)) {
                return OperandTypeEnum.NUM;
            }
            if (DateUtil.verifyDateLegal(operands)) {
                return OperandTypeEnum.DATE;
            }
            return OperandTypeEnum.STR;
        }
    
        private static boolean numCalculate(long operand1, long operand2, OperatorEnum operate){
            switch (operate){
                case LT:
                    return operand1 < operand2;
                case ELT:
                    return operand1 <= operand2;
                case GT:
                    return operand1 > operand2;
                case EGT:
                    return operand1 >= operand2;
                case EQ:
                    return operand1 == operand2;
                case NEQ:
                    return operand1 != operand2;
                default:
                    return true;
            }
        }
    
        private static boolean strCalculate(String operand1, String operand2, OperatorEnum operate){
            switch (operate){
                case EQ:
                    return operand1.equals(operand2);
                case NEQ:
                    return !operand1.equals(operand2);
                case AND:
                    return "true".equals(operand1) && "true".equals(operand2);
                case OR:
                    return "true".equals(operand1) || "true".equals(operand2);
                default:
                    return true;
            }
        }
    
        private static boolean dateCalculate(String operand1, String operand2, OperatorEnum operate){
            switch (operate){
                case LT:
                    return DateUtil.compareDate(operand1, operand2) == -1 ? true : false;
                case ELT:
                    return DateUtil.compareDate(operand1, operand2) <= 0 ? true : false;
                case GT:
                    return DateUtil.compareDate(operand1, operand2) == 1 ? true : false;
                case EGT:
                    return DateUtil.compareDate(operand1, operand2) >= 0 ? true : false;
                case EQ:
                    return DateUtil.compareDate(operand1, operand2) == 0 ? true : false;
                case NEQ:
                    return DateUtil.compareDate(operand1, operand2) != 0 ? true : false;
                default:
                    return true;
            }
        }

    测试代码:

     System.out.println( LogicUtil.parse2("2==3 && 2==2 || 3==3"));
           System.out.println("true == " +  parse2("3>2"));
           System.out.println("false == " + parse2("true == false"));
           System.out.println("false == " + parse2("(3<2||2==2)&&(4==5)"));
           System.out.println("false == " + parse2("(abc==fdg||民事==民事&&嘻嘻==嘻ha)&&(4>2||5<10)"));
           System.out.println("true == " + parse2("()(((abc!=fdg||民事==民事&&嘻嘻==嘻ha)&&(4>2||5<10)))"));
           System.out.println("true == " + parse2("5 < 19"));
           System.out.println("false == " + parse2("5 > 19"));

    测试结果:

  • 相关阅读:
    powermock测试
    一些疑惑
    Java基础总结3
    Java学习路线
    Java基础总结2
    关于我
    翻转单词序列
    和为s的两个数字
    和为s的连续正数序列
    数组中只出现一次的数字
  • 原文地址:https://www.cnblogs.com/lin0/p/13324089.html
Copyright © 2020-2023  润新知