Java解析表达式

时间:2023-02-25 22:41:11

需求

指定一个String表达式,表达式符合给出的运算符规范,比如:2!=2 and 2>=3 or 4<=4,计算出表达式的结果(true or false)。
支持的操作符:(,),and,or,mod,+,-,*,/,>,>=,<,<=,=,!=

思路

  1. 首先要用Java运算符替换表达式中的部分操作符,如and替换为&&,or替换为||,具体如下:

        operatorsMap.put("\\s+and\\s+", "&&");
    operatorsMap.put("\\s+or\\s+", "||");
    operatorsMap.put("\\s+mod\\s+", "%");
    operatorsMap.put("(?<!>|<|\\!|=)=(?!=|>|<|\\!)", "==");
    operatorsMap.put("\\)and\\s+", ")&&");
    operatorsMap.put("\\s+and\\(", "&&(");
    operatorsMap.put("\\)and\\(", ")&&(");
    operatorsMap.put("\\)or\\s+", ")||");
    operatorsMap.put("\\s+or\\(", "||(");
    operatorsMap.put("\\)or\\(", ")||(");
    operatorsMap.put("\\)mod\\s+", ")%");
    operatorsMap.put("\\s+mod\\(", "%(");
    operatorsMap.put("\\)mod\\(", ")%(");
    operatorsMap.put("\\!\\s+\\=", "!=");
    operatorsMap.put("\\>\\s+\\=", ">=");
    operatorsMap.put("\\<\\s+\\=", "<=");
  2. 将替换后的表达式,转化了一个List<Segment>,Segment定义为:

    public Segment(String word,int type){
    this.word = word;//词
    this.type = type;//类型,如DIGIT = 1;LETTER = 2;
    }

    比如(ab+cd)/2 >= 3,解析后的Segment列表为:

    segment1: (
    segment2: ab
    segment3: +
    segment4: cd
    segment5: )
    segment6: /
    segment7: 2
    segment8: space
    segment9: >=
    segment10: space
    segment11: 3
  3. List<Segment>转化为后缀表达式List<String>,其中过滤掉空格(space)Segment

    public void doConvert(Segment segment) {
    int type = segment.getType();
    if (isBarcket(type)) { //括号的处理
    dealBracket(segment);
    } else if (isOperator(type)) {
    dealOperator(segment);//运算符的处理
    } else {
    list.add(segment.getWord());//操作数的处理
    }
    }
  4. 自定义各种运算符的计算规则

        operationMap.put("+", new PlusOperator());
    operationMap.put("-", new MinusOperator());
    operationMap.put("*", new MultipliedOperator());
    operationMap.put("/", new DivideOperator());
    operationMap.put("%", new ModOperator());
    operationMap.put("^", new PowerOperator());
    operationMap.put(">", new GtOperator());
    operationMap.put("<", new LtOperator());
    operationMap.put(">=", new GeOperator());
    operationMap.put("<=", new LeOperator());
    operationMap.put("==", new EqOperator());
    operationMap.put("!=", new NeOperator());
    operationMap.put("&&", new AndOperator());
    operationMap.put("||", new OrOperator());

    //比如加法运算符,PlusOperator:
    public void operator(Deque<String> stack) {
    //操作数出栈,完成运算
    BigDecimal b = new BigDecimal(stack.pop());
    BigDecimal a = new BigDecimal(stack.pop());
    stack.push(a.add(b).stripTrailingZeros().toPlainString());
    }
  5. 计算后缀表达式的值。如果后缀表达式中操作数都是变量名,那么计算之前需要完成值的替换。

    public String compute(List<String> postfix) {
    try {
    Deque<String> stack = new ArrayDeque<>();
    for (String item : postfix) {
    Operator op = operationMap.get(item);
    if (null == op) {
    stack.push(item);
    } else {
    op.operator(stack);
    }
    }
    return stack.pop();
    } catch (Exception e) {
    logger.info(e.getMessage(), e);
    return "ERROR";
    }
    }
  6. 返回布尔值

    //List<String> mustList后缀表达式
    //Map<String, Object> value表达式中变量的值
    public boolean getResult(List<String> mustList, Map<String, Object> value) {
    return Boolean.parseBoolean(compute(replace(mustList, value)));
    }

总结

将表达式处理为后缀表达式,通过栈完成操作数的运算,是个比较经典的小程序,比较考验计算机功底和细节处理。