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

目录
相关文章
|
10天前
|
Java
Java 字符串分割split空字符串丢失解决方案
Java 字符串分割split空字符串丢失解决方案
|
24天前
|
Java
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
25 0
|
10天前
|
XML 数据可视化 前端开发
java正则表达式
java正则表达式
|
6天前
|
Java
Java 14 强势登场:Switch 表达式的进化之路
Java 14 强势登场:Switch 表达式的进化之路
13 0
|
9天前
|
Java 开发者
Java中的Lambda表达式:简洁、灵活的编程利器
在现代软件开发中,编写简洁、高效的代码是至关重要的。Java中的Lambda表达式为开发者提供了一种简洁、灵活的编程方式,使得代码更具可读性和可维护性。本文将探讨Lambda表达式的基本概念、语法结构以及在实际项目中的应用,以帮助读者更好地理解和运用这一强大的编程工具。
5 0
|
10天前
|
存储 Java API
java8新特性 lambda表达式、Stream、Optional
java8新特性 lambda表达式、Stream、Optional
|
20天前
|
存储 算法 JavaScript
Java入门高频考查算法逻辑基础知识3-编程篇(超详细18题1.8万字参考编程实现)
解决这类问题时,建议采取下面的步骤: 理解数学原理:确保你懂得基本的数学公式和法则,这对于制定解决方案至关重要。 优化算法:了解时间复杂度和空间复杂度,并寻找优化的机会。特别注意避免不必要的重复计算。 代码实践:多编写实践代码,并确保你的代码是高效、清晰且稳健的。 错误检查和测试:要为你的代码编写测试案例,测试标准的、边缘情况以及异常输入。 进行复杂问题简化:面对复杂的问题时,先尝试简化问题,然后逐步分析和解决。 沟通和解释:在编写代码的时候清晰地沟通你的思路,不仅要写出正确的代码,还要能向面试官解释你的
32 0
|
20天前
|
Java
java 8 数组转字符串并以逗号分隔
java 8 数组转字符串并以逗号分隔
11 0
|
25天前
|
Java API 开发者
Java中的Lambda表达式及其应用
本文将介绍Java中的Lambda表达式,探讨其在函数式编程中的作用和应用。通过对Lambda表达式的语法、特点以及实际应用场景的详细分析,读者将能够更好地理解并运用Lambda表达式,从而提高代码的简洁性和可读性。
18 1
|
27天前
|
分布式计算 Java 程序员
Java 8新特性之Lambda表达式与Stream API
本文将详细介绍Java 8中的两个重要新特性:Lambda表达式和Stream API。Lambda表达式是Java 8中引入的一种简洁、匿名的函数表示方法,它允许我们将函数作为参数传递给其他方法。而Stream API则是一种新的数据处理方式,它允许我们以声明式的方式处理数据,从而提高代码的可读性和可维护性。通过本文的学习,你将能够掌握Lambda表达式和Stream API的基本用法,以及如何在项目中应用这两个新特性。
30 10