簡體   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