[英]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.