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

目录
相关文章
|
6天前
|
SQL Rust Java
怎么理解Java中的lambda表达式
Lambda表达式是JDK8引入的新语法,用于简化匿名内部类的代码写法。其格式为`(参数列表) -&gt; { 方法体 }`,适用于函数式接口(仅含一个抽象方法的接口)。通过Lambda表达式,代码更简洁灵活,提升Java的表达能力。
|
11天前
|
SQL IDE 算法
《从头开始学java,一天一个知识点》之:运算符与表达式:算术、比较和逻辑运算
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列为你打造Java「速效救心丸」,每天1分钟,地铁通勤、午休间隙即可完成学习。直击高频考点和实际开发中的「坑位」,拒绝冗长概念,每篇都有可运行的代码示例。明日预告:《控制流程:if-else条件语句实战》。
26 6
|
4月前
|
SQL Java 索引
java小工具util系列2:字符串工具
java小工具util系列2:字符串工具
181 83
|
4月前
|
Java 数据库
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
87 26
|
3月前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
4月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
|
4月前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
4月前
|
并行计算 Java 编译器
深入理解Java中的Lambda表达式
在Java 8中引入的Lambda表达式,不仅简化了代码编写,还提升了代码可读性。本文将带你探索Lambda表达式背后的逻辑与原理,通过实例展示如何高效利用这一特性优化你的程序。
|
4月前
|
搜索推荐 Java API
探索Java中的Lambda表达式
本文将深入探讨Java 8引入的Lambda表达式,这一特性极大地简化了代码编写,提高了程序的可读性。通过实例分析,我们将了解Lambda表达式的基本概念、使用场景以及如何优雅地重构传统代码。文章不仅适合初学者,也能帮助有经验的开发者加深对Lambda表达式的理解。
|
4月前
|
Java
探索Java中的Lambda表达式
【10月更文挑战第37天】本文将带你深入理解Java的Lambda表达式,从基础语法到高级特性,通过实例讲解其在函数式编程中的应用。我们还将探讨Lambda表达式如何简化代码、提高开发效率,并讨论其在实际项目中的应用。

热门文章

最新文章