java 实现逻辑表达式解析
java 实现逻辑表达式解析
老黄瓜001
于 2020-07-28 17:16:30 发布
这里的字符串分割使用的默认空格分隔,使用时需要特别注意模板格式。
废话不多说,直接上代码
package com.lunua.parse; import com.alibaba.fastjson.JSONPath; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; public class Parser { enum Op{ GT(">"),GE(">="),LT("<"),LE("<="),EQ("=="),AND("and"),OR("or"),CONTAINS("contains"),CONTAINS_NOT("containsnot"); private String operator; Op(String operator) { this.operator = operator; } public String getOperator() { return operator; } public static Op getByOperator(String operator){ if (operator == null || operator.length() == 0){ return null; } for (Op op: Op.values()) { if (op.getOperator().equalsIgnoreCase(operator)){ return op; } } return null; } } private static final List<String> operator = Lists.newArrayList(Op.GT.getOperator(), Op.GE.getOperator(), Op.LT.getOperator(),Op.LE.getOperator(),Op.EQ.getOperator(),Op.AND.getOperator(),Op.OR.getOperator(),Op.CONTAINS.getOperator(),Op.CONTAINS_NOT.getOperator()); private static Map<String, Integer> operatorMap = null; static { operatorMap = new HashMap<String, Integer>(); operatorMap.put("(", 1); operatorMap.put(")", 1); operatorMap.put("and", 11); operatorMap.put("or", 12); operatorMap.put("==", 7); operatorMap.put("!=", 7); operatorMap.put(">", 6); operatorMap.put(">=", 6); operatorMap.put("<", 6); operatorMap.put("<=", 6); operatorMap.put("contains",14); //operatorMap.put("containsNot",14); operatorMap.put("containsnot",14); } private ArrayList<String> getTokens(String pattern){ String[] splitArr = pattern.split(" "); ArrayList<String> tokens = new ArrayList<String>(); for (String split:splitArr) { String trim = split.trim(); if (trim != null && trim.length() != 0){ tokens.add(trim); } } return tokens; } /** * 计算表达式 * @return * @throws Exception */ public boolean calculate(String pattern,String log) throws Exception { Stack<String> stack = new Stack<String>(); ArrayList<String> tokens = generateReversePolish(getTokens(pattern)); for (String token :tokens) { //如果是操作数则入栈 if (!operatorMap.containsKey(token.toLowerCase())){ stack.push(token); } else{ String right = getValueFromContext(stack.pop(),log); String left = getValueFromContext(stack.pop(), log); stack.push(operate(left, right, token).toString()); } } if (stack.size() == 1){ String valueString = stack.pop(); if (valueString.equalsIgnoreCase("true")){ return true; } } return false; } private String getValueFromContext(String variable, String log) { if (!variable.contains("$")){ return variable; }else { if (variable.equalsIgnoreCase("$")){ return log; }else if (variable.contains("$.")){ //使用jsonpath解析 return String.valueOf(JSONPath.read(log, variable)); } } return null; } private ArrayList<String> generateReversePolish(ArrayList<String> tokens) throws Exception{ //运算符stack, 和 操作数stack Stack<String> operatorStack = new Stack<String>(); Stack<String> numberStack = new Stack<String>(); for (String token : tokens) { //如果是操作数,直接插入到操作数stack if (!operatorMap.containsKey(token.toLowerCase())){ numberStack.push(token); } else{ if (operatorStack.empty()){ operatorStack.push(token); } else{ //如果是右括号 则要找到左括号 并且一次入栈到 操作数栈 if (token.equals(")")){ String popToken = null; try { while( !(popToken = operatorStack.pop()).equals("(") ){ numberStack.push(popToken); } continue; } catch (EmptyStackException e) { throw new Exception("invalid expression: '", e); } } String preOperator = operatorStack.peek(); //如果之前的操作符是( ,则不用比较优先级 当前操作符直接入栈 if (preOperator.equals("(")){ operatorStack.push(token); } //比较操作符优先级, 如果该操作符优先级大于等于 , 则直接入栈 else if (operatorMap.get(token) <= operatorMap.get(preOperator)){ operatorStack.push(token); } //如果该操作符优先级 小于, 则在将该操作符如栈之前 要把栈顶操作符 弹出 插入 操作数栈 else{ numberStack.push( operatorStack.pop()); operatorStack.push(token); } } } } while (!operatorStack.empty()) { numberStack.push( operatorStack.pop()); } ArrayList<String> resArrayList = new ArrayList<String>(); String[] array = numberStack.toArray(new String[]{}); for (int i = 0; i < array.length; i++) { resArrayList.add(array[i]); } return resArrayList; } private Boolean operate(String left,String right,String op){ Op operator = Op.getByOperator(op); if (operator == null){ System.out.println("未知的操作 op = "+op); return false; } Double leftNum =null; Double rightNum = null; switch (operator) { case GT: leftNum = Double.valueOf(left); rightNum = Double.valueOf(right); return leftNum>rightNum; case GE: leftNum = Double.valueOf(left); rightNum = Double.valueOf(right); return leftNum>=rightNum; case LT: leftNum = Double.valueOf(left); rightNum = Double.valueOf(right); return leftNum<rightNum; case LE: leftNum = Double.valueOf(left); rightNum = Double.valueOf(right); return leftNum<=rightNum; case EQ: return left.equals(right); case AND: if (!left.equals("true")){ return false; } if (!right.equals("true")){ return false; } return true; case OR: if (left.equals("true")){ return true; } if (right.equals("true")){ return true; } return false; case CONTAINS: if (left==null || right==null){ return false; } return left.contains(right); case CONTAINS_NOT: if (left==null || right==null){ return false; } return !left.contains(right); default:return false; } } public static void main(String[] args) throws Exception { Parser parser = new Parser(); //System.out.println(parser.calculate("$ contains zhangsan","name=zhangsan")); //System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age > 12 )","{\"name\":\"zhangsan\",\"age\":14}")); System.out.println(parser.calculate("( $.name CONTAINSNOT zhangsan ) and ( $.age > 12 )","{\"name\":\"zhangsaan1\",\"age\":14}")); System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age >= 12 )","{\"name\":\"zhangsan\",\"age\":12}")); System.out.println(parser.calculate("( $.name == zhangsan ) and ( $.age >= 12 )","{\"name\":\"zhangsan\",\"age\":11}")); } }
参考:
https://juejin.im/post/5b9bb590e51d450e7579cf0d
https://gitee.com/wangzhuoa/ExpressionParser
pom依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <version>2.4.0</version> </dependency> 这里json解析使用到了jsonpath
————————————————
版权声明:本文为CSDN博主「老黄瓜001」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33894426/article/details/107640591
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构