1. 程式人生 > 實用技巧 >java實現計算複雜邏輯表示式

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

測試結果: