在java中用字符串表示的逻辑表达式的求值

zazmityj  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(239)

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

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

假设设备已订阅主题“18”和“4”,而未订阅其他主题,则通知对设备无效。如果设备订阅了主题“18”和“reg\ U 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(取决于设备是否订阅),修剪空格,删除引号,将“| |”替换为“|”,将“&&”替换为“&”。。。将缩短的字符串发送到evaluateTopiceExpression()之前。
例如,如果逻辑表达式

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

并且设备订阅了主题“18”、“4”、“reg\u 92779”,checknotificationvalid()调用evaluateTopiceExpression(),使用以下字符串

(1|1)&(1|0)

我测试了很多表达式,代码运行良好。尽管代码运行良好,但我觉得evaluatetopicexpression()有很多代码,而且它不是高效的代码,因为我正在遍历每个字符并递归调用函数。不管怎样,我们可以减少代码并以更有效的方式计算表达式。
注意:我们可以用true或false替换代码,而不是用1或0替换代码

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

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

lrl1mhuk

lrl1mhuk1#

在同一个递归函数中进行解析和求值 boolean evalExpression(String expr) . 这样可以避免递归到冗余树中。如果计算的表达式中有&&运算符的一半为false,则不需要计算另一半。

相关问题