繁体   English   中英

评估在java中表示为字符串的逻辑表达式

[英]Evaluating logical expression which is represented as string in java

我正在为我们的应用程序支持 Kindle 设备上的通知。 我们将从包含主题的服务器接收表达式 sting(主题将在单引号中)。 我们需要通过检查设备是否订阅主题来验证表达式。 下面是逻辑表达式的示例。

('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)

假设设备订阅了主题“18”和“4”并且没有订阅其他主题,则通知对设备无效。 如果设备订阅了主题“18”和“REG_91212”,则通知对设备有效。 我写了下面的代码,它工作正常。

private boolean checkNotificationValid(String leString) {
        // Remove the 'in topics' string. examples of logical expression as below
        // ('4' in topics && '50000' in topics) || ('60' in topics || '3' in topics)
        // ('4' in topics && ('50000' in topics || '60' in topics))
        leString = leString.replaceAll("in topics", "");
        Log.d(TAG, "Logical Expression received : " + leString);
        boolean result = false;

        // Find the topics in the logical expression and check whether the device is subscribed to
        // the topic. If device is subscribed to the topic, replace the topic with 1 or replace the
        // topic with 0. Below is the example.
        // Assume device is subscribed to topics 3 and 4.
        // Expression : ('4' && '50000') || ('60' || '3')
        // After this block of code, it becomes : ('1' && '0') || ('0' || '1')
        StringBuffer buffer = new StringBuffer();
        Pattern p = Pattern.compile("'(.*?)'");
        Matcher m = p.matcher(leString);
        while(m.find()) {
            Log.d(TAG, "LE : " + m.group(1));
            // Check whether the device is subscribed to the topic
            m.appendReplacement(buffer, NotificationUtils.isTopicExistInSharedPref(m.group(1)) ? "1" : "0");
        }

        m.appendTail(buffer);
        leString = buffer.toString();

        // Remove the quotes and spaces from the string, replace '||' with '|', replace '&&' with '&'
        // ('1' && '0') || ('0' || '1') -> (1&0)|(0|1)
        leString = leString.replaceAll("[' ]", "");
        leString = leString.replaceAll("\\|\\|", "|");
        leString = leString.replaceAll("&&", "&");
        Log.d(TAG, "String after changing : " + leString);

        // Evaluate the logical expression
        result = evaluateTopicExpression(leString);

        return result;
    }

    private boolean evaluateTopicExpression(String expString) {
        int len = expString.length();
        int idx = 0;
        boolean result = false;
        int parenCnt = 0;

        // Check for empty expression
        if(len == 0) {
            Log.d(TAG, "empty expression in evaluateTopicExpression");
            return false;
        }

        // If there is only one character it shouldn't be logical operator and shouldn't be
        // parentheses as we are removing parentheses while evaluating the expression.
        if(len == 1) {
            if(expString.charAt(idx) == '0') {
                return false;
            } else {
                return true;
            }
        }

        // Check if the starting character is '('. If it is open parentheses, find the matching
        // closed parentheses. We need to find the exact closed parentheses, extract the string
        // between the parentheses and evaluate that string, concatenate the value to the remaining
        // string and evaluate the string.
        // Example as below.
        // (1|0)&(1&1) -> evaluate 1|0 -> result of 1|0 is 1 -> concatenate the result to remaining
        //                string -> 1&(1&1) -> evaluate the string
        // (1|0)&1 -> evaluate 1|0 -> result of 1|0 is 1 -> concatenate the result to remaining string
        //            -> 1&1 -> evaluate the string
        // (1&(0|1)) -> evaluate 1&(0|1) (This is because once we remove the last parentheses there
        //              is no remaining string after the parentheses)
        if(expString.charAt(idx) == '(') {
            int endIndex = 0;
            parenCnt ++;
            for(int i = idx + 1; i < len; i++) {
                if(expString.charAt(i) == '(') {
                    parenCnt++;
                } else if(expString.charAt(i) == ')') {
                    parenCnt--;
                }
                if(parenCnt == 0) {
                    endIndex = i;
                    break;
                }
            }

            if(parenCnt != 0) {
                Log.d(TAG, "Invalid matching parentheses");
                return false;
            }

            if(endIndex == len - 1) {
                return evaluateTopicExpression(expString.substring(1, endIndex));
            }
            else {
                return evaluateTopicExpression((evaluateTopicExpression(expString.substring(1, endIndex)) ? '1' : '0')
                        + expString.substring(endIndex + 1));
            }
        }

        // If expression string length >= 2, evaluate the first 2 characters in the string and evaluate
        // the remaining string in the next recursive function call
        if(idx + 2 < len) {
            if(expString.charAt(idx + 1) == '|') {
                if(expString.charAt(idx) == '0') {
                    result = evaluateTopicExpression(expString.substring(idx + 2));
                } else {
                    result =  true;
                }
            }

            if(expString.charAt(idx + 1) == '&') {
                if(expString.charAt(idx) == '1') {
                    result = evaluateTopicExpression(expString.substring(idx + 2));
                } else {
                    result = false;
                }
            }
        } else {
            Log.d(TAG, "Error, Invalid logical expression");
            result = false;
        }

        return result;
    }

checkNotificationValid() 函数用 1 或 0 替换主题(基于设备是否订阅/未订阅),修剪空格,删除引号,替换 '||' 使用'|',将'&&' 替换为'&' ... 在将缩短的字符串发送到evaluateTopicExpression() 之前。

例如,如果逻辑表达式是

('18' in topics || '4' in topics) && ('REG_92779' in topics || 'REG_91212' in topics)

并且设备订阅了主题“18”、“4”、“REG_92779”,checkNotificationValid() 使用下面的字符串调用evaluateTopicExpression()

(1|1)&(1|0)

我测试了很多表达式,代码运行良好。 尽管代码运行良好,但我觉得evaluateTopicExpression() 有很多代码并且它不是高效的代码,因为我正在遍历每个字符并递归调用该函数。 无论如何我们可以减少代码并以更有效的方式评估表达式。

注意:我们可以用 true 或 false 代替,而不是用 1 或 0 替换代码

(true||true)&&(true||false)

我需要做的就是通知是否对设备有效。

在同一个递归函数boolean evalExpression(String expr)进行解析和评估。 这样您就可以避免递归到冗余树中。 如果您计算具有 && 运算符的表达式的一半并且它是假的,则不需要计算另一半。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM