java实现简单的字符串解析匹配运算规则引擎

Posted 天涯泪小武

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java实现简单的字符串解析匹配运算规则引擎相关的知识,希望对你有一定的参考价值。

有这样的需求,我有一个map,里面放了一些key-value,自定义了一些规则,如age==24&&name==aom||phone==123456789,希望能有个引擎能判断出这个Map里的值,是否匹配上这个规则,规则里有一些简单的运算,如==、contains等。

规则是动态可变的,这样就可以灵活控制命中了规则的数据能进行一些采集。

我做了一个这样简单的工具,目前可以支持

//规则描述,支持的有:
    //==,如 age==25,name==jerry,字符串也不要加引号
    //!=,和==一样的用法
    //&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
    //contains,如address contains(example),字符串不要加引号
    //matches,如phone matches(\\d+),正则表达式
    //in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
    //isEmpty,是否为空,如address isEmpty
    //isNotEmpty,同isEmpty
    //!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围

这些常用的一些基本规则。

类似于的表达式还有spring的SpEL、mvel这些表达式引擎,但我的场景对性能的要求相当苛刻,规则倒是很简单,是无法接受这些动态规则引擎的高达十几甚至20ms的耗时。对这一个规则匹配的耗时要求不能超过1ms,所以就自己做了一个。

下面是代码

import java.util.Map;

/**
 * 规则校验器
 * @author wuweifeng wrote on 2023/3/7
 * @version 1.0
 */
public class RuleValidator 
    public static boolean validateRule(String rule, Map<String, String> variables) 
        if (rule == null || "".equals(rule)) 
            return false;
        
        // 去除所有空格
        rule = rule.replaceAll("\\\\s+", "");

        //判断表达式是不是取非语句,如" !(a==345) "
        if (rule.startsWith("!(") && rule.endsWith(")")) 
            return !validateRule(rule.substring(2, rule.length() - 1), variables);
        

        // 处理 || 运算符
        if (rule.contains("||")) 
            int index = rule.indexOf("||");
            String left = rule.substring(0, index);
            String right = rule.substring(index + 2);
            return validateRule(left, variables) || validateRule(right, variables);
        

        // 处理 && 运算符
        if (rule.contains("&&")) 
            int index = rule.indexOf("&&");
            String left = rule.substring(0, index);
            String right = rule.substring(index + 2);
            return validateRule(left, variables) && validateRule(right, variables);
        

        // 处理括号运算符
        if (rule.startsWith("(") && rule.endsWith(")")) 
            String subrule = rule.substring(1, rule.length() - 1);
            return validateRule(subrule, variables);
        

        // 处理 == 运算符和 != 运算符
        if (rule.contains("==")) 
            int index = rule.indexOf("==");
            String left = rule.substring(0, index);
            String right = rule.substring(index + 2);
            if (variables.containsKey(left)) 
                return variables.get(left).equals(right);
             else 
                return left.equals(right);
            
         else if (rule.contains("!=")) 
            int index = rule.indexOf("!=");
            String left = rule.substring(0, index);
            String right = rule.substring(index + 2);
            if (variables.containsKey(left)) 
                return !variables.get(left).equals(right);
             else 
                return !left.equals(right);
            
        

        // 处理 contains 运算符和 in 运算符
        if (rule.contains("contains")) 
            int index = rule.indexOf("contains");
            String left = rule.substring(0, index).replace(".", "");
            String value = variables.get(left);
            String right = rule.substring(index + 8).replace("(", "").replace(")", "");
            if (variables.containsKey(left)) 
                return value.contains(right);
             else 
                return false;
            
         else if (rule.contains("in")) 
            int index = rule.indexOf("in");
            String left = rule.substring(0, index);
            String value = variables.get(left);
            String right = rule.substring(index + 2).replace("[", "").replace("]", "");
            if (variables.containsKey(right)) 
                String[] items = variables.get(right).split(",");
                for (String item : items) 
                    if (item.trim().equals(value)) 
                        return true;
                    
                
                return false;
             else 
                String[] items = right.split(",");
                for (String item : items) 
                    if (item.trim().equals(value)) 
                        return true;
                    
                
                return false;
            
        

        // 处理 isEmpty 运算符
        if (rule.contains("isEmpty")) 
            int index = rule.indexOf("isEmpty");
            String left = rule.substring(0, index);
            if (variables.containsKey(left)) 
                return variables.get(left) == null || "".equals(variables.get(left));
             else 
                return false;
            
        

        // 处理 isEmpty 运算符
        if (rule.contains("isNotEmpty")) 
            int index = rule.indexOf("isNotEmpty");
            String left = rule.substring(0, index);
            if (variables.containsKey(left)) 
                return variables.get(left) != null && !"".equals(variables.get(left));
             else 
                return false;
            
        

        // 处理 matches 运算符
        if (rule.contains("matches")) 
            int index = rule.indexOf("matches");
            String left = rule.substring(0, index);
            String right = rule.substring(index + 7);
            if (variables.containsKey(left)) 
                return variables.get(left).matches(right);
             else 
                return left.matches(right);
            
        

        // 如果规则字符串为变量名,则直接查找Map中对应的值
        if (variables.containsKey(rule)) 
            return true;
        

        return false;

    
import java.util.HashMap;
import java.util.Map;

/**
 * @author wuweifeng wrote on 2023/3/7
 * @version 1.0
 */
public class RuleValidatorTest 
    //规则描述,支持的有:
    //==,如 age==25,name==jerry,字符串也不要加引号
    //!=,和==一样的用法
    //&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
    //contains,如address contains(example),字符串不要加引号
    //matches,如phone matches(\\d+),正则表达式
    //in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
    //isEmpty,是否为空,如address isEmpty
    //isNotEmpty,同isEmpty
    //!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围


    public static void main(String[] args) 
        // 初始化变量Map
        Map<String, String> variables = new HashMap<>();
        variables.put("name", "Tom");
        variables.put("age", "25");
        variables.put("email", "tom@example.com");
        variables.put("phone", "123456789");
        variables.put("address", "");

        // 测试用例
        String rule1 = "age==25";
        String rule2 = "age!=25";
        String rule3 = "age==30";
        String rule4 = "age!=30";

        String rule5 = "age==24&&name==aom||phone==123456789";
        String rule6 = "age==25||name==Jerry";

        String rule7 = "address contains(example)";

        //正则表达式
        String rule8 = "phone matches(\\\\d+)";

        String rule9 = "age in [12,1,25]";

        String rule10 = "age in [12,1,25] && name==Tom";

        String rule11 = "address isEmpty";
        String rule12 = "address isNotEmpty";

        String rule13 = "!(address isNotEmpty)";

        // 断言
        System.out.println("rule1-" + RuleValidator.validateRule(rule1, variables));
        System.out.println("rule2-" +  RuleValidator.validateRule(rule2, variables));
        System.out.println("rule3-" +  RuleValidator.validateRule(rule3, variables));
        System.out.println("rule4-" +  RuleValidator.validateRule(rule4, variables));
        System.out.println("------");

        System.out.println("rule5-" +  RuleValidator.validateRule(rule5, variables));
        System.out.println("rule6-" +  RuleValidator.validateRule(rule6, variables));
        System.out.println("rule7-" +  RuleValidator.validateRule(rule7, variables));
        System.out.println("------");

        System.out.println("rule8-" +  RuleValidator.validateRule(rule8, variables));


        System.out.println("------");
        System.out.println("rule9-" + RuleValidator.validateRule(rule9, variables));
        System.out.println("rule10-" + RuleValidator.validateRule(rule10, variables));

        System.out.println("rule11-" + RuleValidator.validateRule(rule11, variables));
        System.out.println("rule12-" + RuleValidator.validateRule(rule12, variables));
        System.out.println("rule13-" + RuleValidator.validateRule(rule13, variables));
    

整体比较简单,不支持复杂的(),最好是平铺的一些规则。

以上是关于java实现简单的字符串解析匹配运算规则引擎的主要内容,如果未能解决你的问题,请参考以下文章

规则表达式引擎框架

java笔试之参数解析(正则匹配)

php模板引擎的原理与简单实例

leetcode如何实现 regex 正则表达式引擎

js模版引擎开发实战以及对eval函数的改进

js模版引擎开发实战以及对eval函数的改进