<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    java求字符串型邏輯表達式的bool值

    Posted on 2007-08-06 12:37 dennis 閱讀(7972) 評論(7)  編輯  收藏 所屬分類: javamy open-source
        這是最近在項目中的一個需求,已知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));
        }

    }

        這個小程序對處理一般的類似區間的規則計算應該還有點用,希望對別人幫助吧。表達式中的邏輯運算符>=和<=可以用=>和=<替代。


    評論

    # re: java求值字符串型邏輯表達式  回復  更多評論   

    2007-08-06 17:04 by Scott.Pan
    看了一遍,不是很明白意在講什么,不過感覺寫的應該挺不錯.

    # re: java求值字符串型邏輯表達式  回復  更多評論   

    2007-08-06 17:27 by dennis
    @Scott.Pan
    汗,看來我的表達能力有問題
    其實就是一個解析邏輯表達式的程序,比如字符串”60<=a<81"
    當a=71的時候,這個字符串執行的結果應該是true,就是用來計算這個的。

    # re: java求字符串型邏輯表達式的bool值  回復  更多評論   

    2007-08-07 09:29 by dreamstone
    呵呵,數據結構的練習啊。 jdk里邊有stack的實現,可以直接使用.

    # re: java求字符串型邏輯表達式的bool值  回復  更多評論   

    2007-08-07 09:54 by dennis
    @dreamstone
    我記的java里的Stack繼承自Vector,多了一大堆不必要的方法,而且是線程安全的吧。我這里只是個局部變量。棧的實現并不是主題。

    # re: java求字符串型邏輯表達式的bool值  回復  更多評論   

    2008-03-13 11:07 by 曲強 Nicky
    可以參考
    http://www.tkk7.com/wqnashqu/archive/2008/02/26/182285.html
    使用樹遍歷,當然腳本也是可選方式之一。

    # re: java求字符串型邏輯表達式的bool值  回復  更多評論   

    2008-03-14 10:44 by dennis
    @曲強 Nicky
    這個謝謝,很早寫的東西了,沒想到還有人關注。呵呵,當時寫的很不成熟,也沒有去找找開源工具,見笑了。目前早已離開那家公司,那個功能當時也是滿足客戶要求的。做這個方法很多,本質上就是一個詞法解析和分析過程,可以利用各種parser generator。

    # re: java求字符串型邏輯表達式的bool值  回復  更多評論   

    2012-01-29 23:05 by whp
    兄弟,簡單的看了一遍。沒看的很細。就略微的和你交流一下吧。優先級考慮進去沒有,復雜一些的邏輯表達式,帶有括號和&& || 操作符的考慮進去沒有。如果考慮進去了我拿來用用,不然還要用開源的。呵呵。
    主站蜘蛛池模板: 午夜寂寞在线一级观看免费| 四虎国产精品免费视| 亚洲人成www在线播放| 国产午夜影视大全免费观看| 东北美女野外bbwbbw免费| 亚洲精品视频免费在线观看| 国产免费人视频在线观看免费 | 亚洲色偷偷色噜噜狠狠99| 亚洲国产一区二区三区| 一区二区三区观看免费中文视频在线播放 | 国精产品一区一区三区免费视频| 亚洲国产成a人v在线| 婷婷亚洲天堂影院| 亚洲精品在线免费看| 无遮挡国产高潮视频免费观看| 中文字幕在线观看亚洲| 无码欧精品亚洲日韩一区夜夜嗨| 日韩午夜理论免费TV影院| 国产成人亚洲精品电影| 亚洲男女一区二区三区| 红杏亚洲影院一区二区三区| AV免费网址在线观看| 中文字幕无码一区二区免费| 亚洲hairy多毛pics大全| 久久99亚洲网美利坚合众国| 亚洲性日韩精品一区二区三区 | 国产福利免费视频 | 国产在线国偷精品免费看| 亚洲伊人久久大香线蕉AV| 亚洲AV无码成人网站久久精品大 | 国产∨亚洲V天堂无码久久久| 日韩人妻无码免费视频一区二区三区| 国产精品免费一区二区三区四区| 精品视频免费在线| 亚洲邪恶天堂影院在线观看| 亚洲国产成人久久一区WWW| 日韩人妻无码精品久久免费一| 成人A毛片免费观看网站| 久久久久亚洲国产AV麻豆| 亚洲国产精品国自产拍电影| 一级毛片直播亚洲|