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

    xiaoxiao2024-05-08  117

    这是最近在项目中的一个需求,已知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

    最新回复(0)