java求字符串型逻辑表达式的bool值

简介:
  这是最近在项目中的一个需求,已知a=3,求字符串"a<=2"的值,也就是应该返回false。这个问题可大可小,就我们的应用场景也就是用来让用户自定义变量区间,比如类似下面这样的规则:
a<=2    返回积分系数1.0
2<a<=5  返回积分系数1.1
a>5     返回积分系数1.2

    如果用switch写死在代码中,以后要修改规则实在是很麻烦的事情,用户也希望能自己维护这样些区间值。于是我想就让用户自己输入这样的表达式和变量的值保存在数据库中,然后计算的时候由系统来解析表达式并求值。问题就归结到求值字符串型逻辑表达式。这个问题恰好是规则引擎的应用领域,可我们的系统已经上线蛮久了,从维护角度也不希望再引入新的开源工具,况且也就这么一个地方用到。如果是算术表达式(比如2+3之类)可以直接扔进数据库执行即可,逻辑表达式倒是可以通过调用脚本语言来eval,但是同样是考虑后期维护问题,也不想引入beanshell、groovy的脚本语言。所以,我就自己写了个parser用于求值。
    基本原理就是维护两个栈:操作数栈和操作符号栈,解析和求值的过程就是入栈和出栈操作。首先使用ArrayList实现一个栈,很容易的事情:
class  Stack {
    
protected  java.util.ArrayList pool  =   new  java.util.ArrayList();

    
public  Stack() {
    }

    
public  Stack( int  n) {
        pool.ensureCapacity(n);
    }

    
public   void  clear() {
        pool.clear();
    }

    
public   boolean  isEmpty() {
        
return  pool.isEmpty();
    }

    
public   int  size() {
        
return  pool.size();
    }

    
public  Object topEl() {
        
if  (isEmpty())
            
throw   new  java.util.EmptyStackException();
        
return  pool.get(pool.size()  -   1 );
    }

    
public  Object pop() {
        
if  (isEmpty())
            
throw   new  java.util.EmptyStackException();
        
return  pool.remove(pool.size()  -   1 );
    }

    
public   void  push(Object el) {
        pool.add(el);
    }

    
public  String toString() {
        
return  pool.toString();
    }
}

    然后看看ExpressionParser.java,原理已经列上,注释也有,使用了单例模式,就请自己看了:
package  net.rubyeye.codelib.util;

/**
 * <p>类说明:用于表达式与实际值的比较</p>
 * <p>注意事项:</p>
 * <pre></pre>
 * <p>创建日期:Aug 6, 2007 10:18:58 AM</p>
 * <p>文件名:ExpressionParser.java</p>
 * 
@author :庄晓丹
 * 
@version  $Id:$
 
*/
public   class  ExpressionParser {
    
private   static   final   boolean  DEBUG  =   true ;

    
private   static  ExpressionParser parser  =   new  ExpressionParser();

    
private  ExpressionParser() {

    }

    
public   static  ExpressionParser getInstance() {
        
return  parser;
    }

    
public   boolean  fireRule(String expression,  double  fact) {
        traceCalculate(
" \nexpression: "   +  expression);
        expression 
=  expression.replace( " \n|\r " "" ).trim();
        
char [] chars  =  expression.toCharArray();
        
return  parseExpression(fact, chars);
    }

    
/**
     * 
@param  fact
     * 
@param  operatorsStack
     * 
@param  operandsStack
     * 
@param  chars
     * 
@param  operand
     * 
@param  operator
     * 
@return
     
*/
    
private   boolean  parseExpression( double  fact,  char [] chars) {
        
boolean  result  =   true ;
        String operand 
=   "" ;
        String operator 
=   "" ;
        Stack operatorsStack 
=   new  Stack();
        Stack operandsStack 
=   new  Stack();
        
for  ( int  i  =   0 ; i  <  chars.length; i ++ ) {
            
char  token  =  chars[i];
            traceCalculate(
" token: "   +  token);
            
if  (Character.isDigit(token)  ||  token  ==   ' . ' ) {
                
if  ( ! operator.equals( "" )) {
                    traceCalculate(
" push operator: "   +  operator);
                    
//     将操作符放入操作符号栈
                    operatorsStack.push(operator);
                    operator 
=   "" ;

                }
                operand 
+=  token;
                result 
=  checkTail(fact, operatorsStack, operandsStack,
                        chars.length, operand, result, i);
                
continue ;
            } 
else   if  (Character.isLetter(token)) {
                
if  ( ! operator.equals( "" )) {
                    traceCalculate(
" push operator: "   +  operator);
                    
//     将操作符放入操作符号栈
                    operatorsStack.push(operator);
                    operator 
=   "" ;
                }
                operand 
=  String.valueOf(token);
                result 
=  checkTail(fact, operatorsStack, operandsStack,
                        chars.length, operand, result, i);
                
// 将操作数放入操作数栈
                operandsStack.push(operand);
                traceCalculate(
" push operand: "   +  token);
                operand 
=   "" ;
                
continue ;
            } 
else  {
                
if  ( ! operatorsStack.isEmpty()  &&   ! operandsStack.isEmpty()) {
                    
// 当前操作数是字母(变量),已存入栈,因此需要取出
                     if  (operand.equals( "" )) {
                        operand 
=  (String) operandsStack.pop();
                        result 
=  result
                                
&&  calculatePerfomance(operatorsStack,
                                        operandsStack, operand, fact);
                        
// 当前操作数是数字    
                    }  else  {
                        result 
=  result
                                
&&  calculatePerfomance(operatorsStack,
                                        operandsStack, operand, fact);

                    }
                }

                
if  ( ! operand.equals( "" )) {
                    result 
=  checkTail(fact, operatorsStack, operandsStack,
                            chars.length, operand, result, i);
                    
// 将操作数放入操作数栈
                    operandsStack.push(operand);
                    traceCalculate(
" push2 operand: "   +  operand);
                    operand 
=   "" ;
                }

                operator 
+=  token;
                
continue ;
            }

        }
        
return  result;
    }

    
/**
     * 判断是否已经到表达式尾端,如果是,计算
     * 
@param  fact
     * 
@param  operatorsStack
     * 
@param  operandsStack
     * 
@param  chars
     * 
@param  operand
     * 
@param  result
     * 
@param  i
     * 
@return
     
*/
    
private   boolean  checkTail( double  fact, Stack operatorsStack,
            Stack operandsStack, 
int  chars_length, String operand,
            
boolean  result,  int  i) {
        
if  (i  ==  chars_length  -   1 ) {
            result 
=  result
                    
&&  calculatePerfomance(operatorsStack, operandsStack,
                            operand, fact);
        }
        
return  result;
    }

    
private   void  displayStack(String name,Stack stack) {
        
if  (DEBUG) {
            
for  ( int  i  =   0 ; i  <  stack.pool.size(); i ++ )
                System.out.println(name
+ stack.pool.get(i));
        }
    }

    
private   boolean  calculatePerfomance(Stack operatorsStack,
            Stack operandsStack, String currentOperand, 
double  fact) {
        traceCalculate(
" 开始计算 " );
        displayStack(
" operators stack: " ,operatorsStack);
        displayStack(
" operands stack: " ,operandsStack);
        traceCalculate(
" currentOperand= "   +  currentOperand);
        String operator 
=  (String) operatorsStack.pop();
        
double  lastOperand  =  coverOperandToDouble((String) operandsStack.pop(),
                fact);
        
double  nextOperand  =  coverOperandToDouble(currentOperand, fact);
        
boolean  result  =   true ;
        
if  (operator.equals( " == " ))
            
return  lastOperand  ==  nextOperand;
        if (operator.indexOf("=") >= 0)
            hasEqual = true;
        
char [] operators  =  operator.toCharArray();
        
for  ( int  i  =   0 ; i  <  operators.length; i ++ ) {
            
switch  (operators[i]) {
            
case   ' < ' :
                result 
=  result  &&  (lastOperand  <  nextOperand);
                
break ;
            
case   ' = ' :
                
// result为false,也就是小于,大于符号不满足的时候,判断等号是否成立
                 if  ( ! result)
                    result 
=  (lastOperand  ==  nextOperand);
                
break ;
            
case   ' > ' :
                result 
=  result  &&  (lastOperand  >  nextOperand);
                
break ;
            }
        }
        if ((!result) && hasEqual)
            result = lastOperand == nextOperand;
        
return  result;

    }

    
/**
     * 用于debug
     
*/
    
private   void  traceCalculate(String info) {
        
if  (DEBUG)
            System.out.println(info);
    }

    
private   double  coverOperandToDouble(String operand,  double  fact) {
        
// 如果是字母,也就是变量,返回fact变量
         if  (Character.isLetter(operand.toCharArray()[ 0 ]))
            
return  fact;
        
else
            
return  Double.parseDouble(operand);
    }
}
    通过DEBUG变量来决定是否输出计算过程,你可以设置为true来看看某个表达式的计算过程。附上单元测试来看看怎么用:
package  net.rubyeye.codelib.util;

/**
 * 测试表达式计算
 
*/
import  junit.framework.TestCase;

public   class  ExpressionCalculateTest  extends  TestCase {
    String exp1,exp2,exp3, exp4;
    
double  v1, v2, v3, v4, v5;

    ExpressionParser parser 
=   null ;

    
protected   void  setUp()  throws  Exception {
        exp1 
=   " a<80 " ;
        exp2 
=   " 80<=a<81 " ;
        exp3 
=   " 81<=a<=82 " ;
        exp4 
=   " a>=90 " ;
        v1 
=   70.0 ;
        v2 
=   81.2 ;
        v3 
=   80 ;
        v4 
=   90 ;
        v5 
=   92 ;
        parser 
=  ExpressionParser.getInstance();
    }

    
public   void  testFireRule()  throws  Exception {
        assertFalse(parser.fireRule(exp1, v4));
        assertTrue(parser.fireRule(exp1, v1));
        assertFalse(parser.fireRule(exp1, v3));
        assertFalse(parser.fireRule(exp2, v2));
        assertTrue(parser.fireRule(exp2, v3));
        assertFalse(parser.fireRule(exp2, 
82 ));
        assertTrue(parser.fireRule(exp3, v2));
        assertTrue(parser.fireRule(exp4, v4));
        assertFalse(parser.fireRule(exp4, v1));
        assertTrue(parser.fireRule(exp4, v5));
        assertTrue(parser.fireRule(
" b==100.00 " 100.0 ));
        assertTrue(parser.fireRule(
" c==0.00 " 0 ));
        assertTrue(parser.fireRule(
" 60<=c<=80 " 79.9 ));
        assertFalse(parser.fireRule(
" 60<=50<=80 " 0.0 ));
        assertTrue(parser.fireRule(
" 60<=79<=80 " 0.0 ));
        assertFalse(parser.fireRule(
" 60<=99<=80 " 0.0 ));
        
        assertTrue(parser.fireRule(
" 60<=80<=90<100 " 0.0 ));
        assertFalse(parser.fireRule(
" 60<=99<=80<100 " 0.0 ));
        assertTrue(parser.fireRule("10=<a=<30", 25));
    }

}


    这个小程序对处理一般的类似区间的规则计算应该还有点用,希望对别人帮助吧。表达式中的逻辑运算符>=和<=可以用=>和=<替代。

文章转自庄周梦蝶  ,原文发布时间 2007-08-06

目录
相关文章
|
存储 Java C++
【==是判断相等吗?---错辣】C++和JAVA中判断字符串值相等的区别
C++与JAVA之间存在着一些区别,我们需要重视区别才能更好地灵活学习和运用不同的编程语言。 总之,C++中可以利用==来判断两个字符串的值是否相等;而JAVA中必须使用String类的成员函数equals()(区分大小写)和equalsIgnoreCase()(不区分大小写)来判断两个字符串的值是否相等。
122 0
Java 将带有小数点的字符串转成Integer类型数值
Java 将带有小数点的字符串转成Integer类型数值
1408 0
Java 将带有小数点的字符串转成Integer类型数值
|
4月前
|
Java
java基础(10)数据类型中的整数类型
Java中的整数类型包括byte、short、int和long。整数字面值默认为int类型,加L表示long类型。整数字面值可以是十进制、八进制(0开头)或十六进制(0x开头)。小容量类型(如int)可自动转换为大容量类型(如long),但大容量转小容量需强制转换,可能导致精度损失。
63 2
|
7月前
|
Java 数据处理 Apache
探讨Java中判断String类型为空和null的方法
探讨Java中判断String类型为空和null的方法
84 1
|
6月前
|
存储 Python
语音输入,python数据类型,type()用来查看数据类型,数据类型转换,int(x)转整数,float(x)转换为浮点数,str(x),将对象转为字符串,标识符,标识符不允许使用关键字,关键字参考
语音输入,python数据类型,type()用来查看数据类型,数据类型转换,int(x)转整数,float(x)转换为浮点数,str(x),将对象转为字符串,标识符,标识符不允许使用关键字,关键字参考
|
7月前
|
Java
Java中判断String类型为空和null的方法
Java中判断`String`变量是否为空或`null`需用`== null`和`.isEmpty()`。示例代码提供两种方法:`main`方法直接判断,`safeGetString`方法提供默认值。当输入为`null`或空时,返回默认值。代码包含三个测试案例,分别处理`null`、空字符串和非空非`null`字符串。
144 0
|
存储 SQL Java
Java变量、数据类型和运算符 1
Java变量、数据类型和运算符
79 0
|
存储 Java
java变量、数据类型和运算符-3
事实上,自动类型转换并非所有情况下都有效。如果不满足上述条件,当必须将double类型变量的值赋给一个int类型变量时,该如何进行转换呢?这时系统就不会完成自动类型转换了。
96 0
|
存储 SQL Java
java变量、数据类型和运算符-2
2.1.4 变量命名规则 旅馆可以随心所欲地给房间命名,可以是数字"1001",也可以是一些有趣的名称,如"美国总统"、"英国女王"、"埃塞俄比亚王妃"等。但是在给变量命名时,就要受到一些约束,如表2-3所示。那么什么样的名称才正确呢?
87 0
|
存储 Java
java变量、数据类型和运算符-4
练习——升级“我行我素购物管理系统”,实现打印购物小票和计算积分功能
160 0