<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"的值,也就是應(yīng)該返回false。這個問題可大可小,就我們的應(yīng)用場景也就是用來讓用戶自定義變量區(qū)間,比如類似下面這樣的規(guī)則:
    a<=2    返回積分系數(shù)1.0
    2<a<=5  返回積分系數(shù)1.1
    a>5     返回積分系數(shù)1.2

        如果用switch寫死在代碼中,以后要修改規(guī)則實在是很麻煩的事情,用戶也希望能自己維護這樣些區(qū)間值。于是我想就讓用戶自己輸入這樣的表達式和變量的值保存在數(shù)據(jù)庫中,然后計算的時候由系統(tǒng)來解析表達式并求值。問題就歸結(jié)到求值字符串型邏輯表達式。這個問題恰好是規(guī)則引擎的應(yīng)用領(lǐng)域,可我們的系統(tǒng)已經(jīng)上線蠻久了,從維護角度也不希望再引入新的開源工具,況且也就這么一個地方用到。如果是算術(shù)表達式(比如2+3之類)可以直接扔進數(shù)據(jù)庫執(zhí)行即可,邏輯表達式倒是可以通過調(diào)用腳本語言來eval,但是同樣是考慮后期維護問題,也不想引入beanshell、groovy的腳本語言。所以,我就自己寫了個parser用于求值。
        基本原理就是維護兩個棧:操作數(shù)棧和操作符號棧,解析和求值的過程就是入棧和出棧操作。首先使用ArrayList實現(xiàn)一個棧,很容易的事情:
    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,原理已經(jīng)列上,注釋也有,使用了單例模式,就請自己看了:
    package net.rubyeye.codelib.util;

    /**
     * <p>類說明:用于表達式與實際值的比較</p>
     * <p>注意事項:</p>
     * <pre></pre>
     * <p>創(chuàng)建日期: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);
                    
    //將操作數(shù)放入操作數(shù)棧
                    operandsStack.push(operand);
                    traceCalculate(
    "push operand:" + token);
                    operand 
    = "";
                    
    continue;
                } 
    else {
                    
    if (!operatorsStack.isEmpty() && !operandsStack.isEmpty()) {
                        
    //當(dāng)前操作數(shù)是字母(變量),已存入棧,因此需要取出
                        if (operand.equals("")) {
                            operand 
    = (String) operandsStack.pop();
                            result 
    = result
                                    
    && calculatePerfomance(operatorsStack,
                                            operandsStack, operand, fact);
                            
    //當(dāng)前操作數(shù)是數(shù)字    
                        } else {
                            result 
    = result
                                    
    && calculatePerfomance(operatorsStack,
                                            operandsStack, operand, fact);

                        }
                    }

                    
    if (!operand.equals("")) {
                        result 
    = checkTail(fact, operatorsStack, operandsStack,
                                chars.length, operand, result, i);
                        
    //將操作數(shù)放入操作數(shù)棧
                        operandsStack.push(operand);
                        traceCalculate(
    "push2 operand:" + operand);
                        operand 
    = "";
                    }

                    operator 
    += token;
                    
    continue;
                }

            }
            
    return result;
        }

        
    /**
         * 判斷是否已經(jīng)到表達式尾端,如果是,計算
         * 
    @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變量來決定是否輸出計算過程,你可以設(shè)置為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));
        }

    }

        這個小程序?qū)μ幚硪话愕念愃茀^(qū)間的規(guī)則計算應(yīng)該還有點用,希望對別人幫助吧。表達式中的邏輯運算符>=和<=可以用=>和=<替代。


    評論

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

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

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

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

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

    2007-08-07 09:29 by dreamstone
    呵呵,數(shù)據(jù)結(jié)構(gòu)的練習(xí)啊。 jdk里邊有stack的實現(xiàn),可以直接使用.

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

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

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

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

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

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

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

    2012-01-29 23:05 by whp
    兄弟,簡單的看了一遍。沒看的很細。就略微的和你交流一下吧。優(yōu)先級考慮進去沒有,復(fù)雜一些的邏輯表達式,帶有括號和&& || 操作符的考慮進去沒有。如果考慮進去了我拿來用用,不然還要用開源的。呵呵。
    主站蜘蛛池模板: 在线视频观看免费视频18| 亚洲另类精品xxxx人妖| 日韩免费a级在线观看| 嫩草在线视频www免费观看| 污污视频网站免费观看| 亚洲娇小性xxxx色| 18亚洲男同志videos网站| 国产日产亚洲系列| 免费一级大黄特色大片| 成人毛片免费观看视频大全| **真实毛片免费观看| 免费看男人j放进女人j免费看| eeuss影院ss奇兵免费com| 亚洲精品中文字幕| 亚洲国产视频久久| 亚洲成人免费电影| 精品亚洲成a人片在线观看少妇| 亚洲欧洲自拍拍偷午夜色无码| 四虎精品亚洲一区二区三区| 四色在线精品免费观看| 精品久久久久国产免费| 国内精品免费麻豆网站91麻豆| 鲁大师在线影院免费观看| 久章草在线精品视频免费观看| 久久久久久国产a免费观看不卡| 老司机免费午夜精品视频| 极品色天使在线婷婷天堂亚洲| 亚洲а∨精品天堂在线| 亚洲色成人WWW永久在线观看| 亚洲综合校园春色| 亚洲娇小性xxxx| 亚洲av无码专区国产不乱码| 亚洲色大成网站www| 亚洲国产AV一区二区三区四区| 亚洲人成网站色在线观看| 久久精品国产亚洲AV忘忧草18 | 337P日本欧洲亚洲大胆艺术图| 亚洲精品无码久久久久秋霞| 亚洲熟女综合一区二区三区| 亚洲精品自偷自拍无码| 美女又黄又免费的视频|