java代碼執行字符串中的邏輯運算

方式一:

public class Test
{
  public static void main(String[] args) throws Exception {
         String str = "(a or b) and c";
         str = str.replaceAll("or", "||");
         str = str.replaceAll("and", "&&");
         System.out.println(str);
         ScriptEngineManager manager = new ScriptEngineManager();
         ScriptEngine engine = manager.getEngineByName("js");
         engine.put("a",true);
         engine.put("b",false);
         engine.put("c",true);        
         Object result = engine.eval_r(str);
         System.out.println("結果類型:" + result.getClass().getName() + ",計算結果:" + result);

     }
}

這種方式使用js的方式進行運算,使用較簡單,但是當運算double類型的四則運算時結果會出現循環小數,運算結果會出現問題.

方式二(能夠保證四則運算精度):

/**
 * @Project:      BizRule     
 * @File:         org.coffeesweet.util.MathExpress.java
 * @Author:       coffeesweet
 * @Date:         2011-3-28
 * @Description:  2011 coffeesweet Inc. All rights reserved.
 */
package org.coffeesweet.util;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



/**
 * @author coffeesweet
 * +,-,*,/四則運算的表達式逆波蘭解析計算類,精確計算,應用BigDecimal類處理
 * 支持負數,但規範除整個表達式第一個數爲負數時可以不出現在'('後,其它表達式中間任何位置的
 * 負數必須出現在'('後,即:用括號括起來。比如:-3+(-2+1)*10或-3+((-2)+1)*10或(-3)+(-2+1)*10或(-3)+((-2)+1)*10
 */
public class MathExpress {
    /**
     * +
     */
    private final static String OP1 = "+";

    /**
     * -
     */
    private final static String OP2 = "-";

    /**
     * *
     */
    private final static String OP3 = "*";

    /**
     * /
     */
    private final static String OP4 = "/";

    /**
     * ^
     */
//    private final static String OP5 = "^";

    /**
     * %
     */
//    private final static String OP6 = "%";
    /**
     * (
     */
    private final static String OPSTART = "(";

    /**
     * )
     */
    private final static String OPEND = ")";

    /**
     * !用來替代負數前面的'-'
     */
//    private final static String NEGATIVESING = "!";

    /**
     * !用來替代負數前面的'+'
     */
//    private final static String PLUSSING = "@";

    /**
     * '#'用來代表運算級別最低的特殊字符
     */
//    private final static String LOWESTSING = "#";

    //最原始的四則運算式
    private String expBase;

    //經過初始化處理後的四則運算式
    private String expInited;

    //精度
    private int precision=10;

    //取捨模式
    private RoundingMode roundingMode=RoundingMode.HALF_UP;

    //精度上下文
    private MathContext mc;

    //四則運算解析
    private List<String> expList = new ArrayList<String>();

    //存放逆波蘭表達式
    private List<String> rpnList = new ArrayList<String>();

    public MathExpress(){
    }

    public MathExpress(String expBase) {
        init(expBase,this.precision,this.roundingMode);
    }

    public MathExpress(String expBase,int precision,RoundingMode roundingMode){
        init(expBase,precision,roundingMode);
    }

    public void init(String expBase,int precision,RoundingMode roundingMode){
        this.expBase = expBase;
        this.precision = precision;
        this.roundingMode = roundingMode;
        this.mc = new MathContext(precision,roundingMode);
        this.expInited = initExpress(expBase);

        StringTokenizer st = new StringTokenizer(this.expInited,"+-*/^%()",true);
        while(st.hasMoreElements()){
            this.expList.add(st.nextElement().toString().trim());
        }

        this.rpnList = initRPN(this.expList);


    }

    /**
     * @return the expBase
     */
    public String getExpBase() {
        return expBase;
    }

    /**
     * @param expBase the expBase to set
     */
    public void setExpBase(String expBase) {
        this.expBase = expBase;
    }

    /**
     * @return the expInited
     */
    public String getExpInited() {
        return expInited;
    }

    /**
     * @param expInited the expInited to set
     */
    public void setExpInited(String expInited) {
        this.expInited = expInited;
    }

    /**
     * @return the precision
     */
    public int getPrecision() {
        return precision;
    }

    /**
     * @param precision the precision to set
     */
    public void setPrecision(int precision) {
        this.precision = precision;
    }

    /**
     * @return the roundingMode
     */
    public RoundingMode getRoundingMode() {
        return roundingMode;
    }

    /**
     * @param roundingMode the roundingMode to set
     */
    public void setRoundingMode(RoundingMode roundingMode) {
        this.roundingMode = roundingMode;
    }

    /**
     * @return the expList
     */
    public List<String> getExpList() {
        return expList;
    }

    /**
     * @param expList the expList to set
     */
    public void setExpList(List<String> expList) {
        this.expList = expList;
    }

    /**
     * @return the rpnList
     */
    public List<String> getRpnList() {
        return rpnList;
    }

    /**
     * @param rpnList the rpnList to set
     */
    public void setRpnList(List<String> rpnList) {
        this.rpnList = rpnList;
    }

    /**
     * @return the mc
     */
    public MathContext getMc() {
        return mc;
    }

    /**
     * @param mc the mc to set
     */
    public void setMc(MathContext mc) {
        this.mc = mc;
    }

    /**
     * 去除空白字符和在負號'-'前加'0',便於後面的StringTokenizer
     * @param exp
     * @return
     */
    private static String initExpress(String exp){
        String reStr = null;
        reStr = exp.replaceAll("\\s", "");
        if(reStr.startsWith("-")){
            reStr = "0"+reStr;
        }
        reStr = reStr.replaceAll("\\(\\-", "(0-");
        return reStr;
    }

    /**
     * 是否是整數或是浮點數,但默認-05.15這種也認爲是正確的格式
     * @param str
     * @return
     */
    private boolean isNumber(String str){
        Pattern p = Pattern.compile("^(-?\\d+)(\\.\\d+)?$");
        Matcher m = p.matcher(str);
        boolean isNumber = m.matches();
        return isNumber;
    }

    /**
     * 設置優先級順序()設置與否無所謂
     * @param sign
     * @return
     */
    private int precedence(String str){
        char sign = str.charAt(0);
        switch(sign){
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            case '^':
            case '%':
                return 3;
            case '(':
            case ')':
//          case '#':
            default:
                return 0;

        }
    }

    /**
     * 轉變爲逆波蘭表達式
     * @param strList
     * @return
     */
    public List<String> initRPN(List<String> strList){
        List<String> returnList = new ArrayList<String>();
        //用來存放操作符的棧
        Stack stack = new Stack();
//      stack.push(LOWESTSING);
        int length = strList.size();
        for(int i=0;i<length;i++ ){
            String str = strList.get(i);
            if(isNumber(str)){
                returnList.add(str);
            }else{
                if(str.equals(OPSTART)){
                    //'('直接入棧
                    stack.push(str);
                }else if(str.equals(OPEND)){
                    //')'
                    //進行出棧操作,直到棧爲空或者遇到第一個左括號   
                    while (!stack.isEmpty()) {   
                        //將棧頂字符串做出棧操作   
                        String tempC = stack.pop();   
                        if (!tempC.equals(OPSTART)) {   
                            //如果不是左括號,則將字符串直接放到逆波蘭鏈表的最後   
                            returnList.add(tempC);   
                        }else{   
                            //如果是左括號,退出循環操作   
                            break;   
                        }   
                    }   
                }else{
                    if (stack.isEmpty()) {
                        //如果棧內爲空   
                        //將當前字符串直接壓棧   
                        stack.push(str);   
                    }else{
                        //棧不空,比較運算符優先級順序
                        if(precedence(stack.top())>=precedence(str)){
                            //如果棧頂元素優先級大於當前元素優先級則
                            while(!stack.isEmpty() && precedence(stack.top())>=precedence(str)){
                                returnList.add(stack.pop());
                            }
                        }
                        stack.push(str);
                    }
                }
            }
        }
        //如果棧不爲空,則將棧中所有元素出棧放到逆波蘭鏈表的最後   
        while (!stack.isEmpty()) {
            returnList.add(stack.pop());
        }
        return returnList;
    }

    /**
     * 計算逆波蘭表達式
     * @param rpnList
     * @return
     */
    public String caculate(List<String> rpnList){
        Stack numberStack = new Stack();   

        int length=rpnList.size();   
        for(int i=0;i<length;i++){   
            String temp=rpnList.get(i);   
            if(isNumber(temp)){   
                numberStack.push(temp);   
            }else{   
                BigDecimal tempNumber1 = new BigDecimal(numberStack.pop(),this.mc);

                BigDecimal tempNumber2 = new BigDecimal(numberStack.pop(),this.mc);

                BigDecimal tempNumber = new BigDecimal("0",this.mc);

                if(temp.equals(OP1)){   
                    tempNumber=tempNumber2.add(tempNumber1);   
                }else if(temp.equals(OP2)){   
                    tempNumber=tempNumber2.subtract(tempNumber1);   
                }else if(temp.equals(OP3)){   
                    tempNumber=tempNumber2.multiply(tempNumber1);   
                }else if(temp.equals(OP4)){   
                    tempNumber=tempNumber2.divide(tempNumber1,   
                            precision,   
                            roundingMode);   
                }  
                numberStack.push(tempNumber.toString());   

            }   
        }   

        return numberStack.pop();

    }
    /**
     * 按照類的缺省參數進行計算
     * @return
     */
    public String caculate(){
        return caculate(this.rpnList);
    }

    /**
     * 數字條件表達式精確比較
     * eg: "3.0>2"   "1<5"   "1==5"   "1!=5"   "(1.0+2)>3"  "((-0.9+3)>=2. 1)"
     * 不支持&&,||等連接符
     * @param str
     * @return
     */
    public static boolean compareTo(String strParm){
        boolean reBoolean = false;
        boolean isParentheses = false;//標記是否有()括上整個字符串
        String str = initExpress(strParm);
        Pattern p = Pattern.compile("^\\([\\s\\S]*\\)$");
        Matcher m = p.matcher(str);
        isParentheses = m.matches();
        if(-1==str.indexOf(">=")&&-1==str.indexOf("<=")&&-1==str.indexOf("==")&&-1==str.indexOf("!=")){
            if(-1==str.indexOf(">")&&-1==str.indexOf("<"))
            throw new IllegalArgumentException("異常:條件表達式不正確!");
        }
        if(-1 != str.indexOf(">=")){
            String[] strTemps = str.split(">=");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( -1 == r ){
                reBoolean = false;
            }else{
                reBoolean = true;
            }
        }else if(-1 != str.indexOf("<=")){
            String[] strTemps = str.split("<=");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( 1 == r ){
                reBoolean = false;
            }else{
                reBoolean = true;
            }
        }else if(-1 != str.indexOf("==")){
            String[] strTemps = str.split("==");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( 0 == r ){
                reBoolean = true;
            }else{
                reBoolean = false;
            }
        }else if(-1 != str.indexOf("!=")){
            String[] strTemps = str.split("!=");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( 0 != r ){
                reBoolean = true;
            }else{
                reBoolean = false;
            }
        }else if((-1 != str.indexOf(">")) && (-1 == str.indexOf("="))){
            String[] strTemps = str.split(">");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( 1 == r ){
                reBoolean = true;
            }else{
                reBoolean = false;
            }
        }else if((-1 != str.indexOf("<")) && (-1 == str.indexOf("="))){
            String[] strTemps = str.split("<");
            if(isParentheses){
                strTemps[0] = strTemps[0] + ")";
                strTemps[1] = "(" + strTemps[1];
            }
            int r = new BigDecimal((new MathExpress(strTemps[0]).caculate())).compareTo(new BigDecimal((new MathExpress(strTemps[1]).caculate())));
            if( -1 == r ){
                reBoolean = true;
            }else{
                reBoolean = false;
            }
        }
        return reBoolean;
    }

    public static void main(String...args){
//      MathExpress me = new MathExpress("-(-0.5+0.1)*10+2",10,RoundingMode.HALF_UP);
//      System.out.println(me.getExpList());
//      List<String> tempList = me.initRPN(me.getExpList());
//      System.out.println(tempList);
//      String resultStr = me.caculate(tempList);
//      System.out.println(resultStr);

        MathExpress me = new MathExpress("-(-1.5000000003+0.1)*10+2");
        String resultStr = me.caculate();
        BigDecimal bd = new BigDecimal(resultStr);
        BigDecimal bd2 = bd.setScale(2, RoundingMode.HALF_UP);
        System.out.println(me.caculate());
        System.out.println(bd.toString());
        System.out.println(bd.scale());
        System.out.println(bd2.toString());
        System.out.println(bd2.scale());

//      System.out.println("------------------------------------");
//      Pattern p = Pattern.compile("^\\([\\s\\S]*\\)$");//匹配類似以'('開頭')'結尾的字符串
//      Matcher m = p.matcher("(2.  0>2.22)");
//      System.out.println(m.matches());

        boolean reBoolean = MathExpress.compareTo("((-8.0+3)>=2. 1)");
        System.out.println(reBoolean);

    }

    /**
     * 棧
     */
    private class Stack {

        LinkedList<String> stackList = new LinkedList<String>();

        public Stack() {

        }

        /**
         * 入棧
         * @param expression
         */
        public void push(String expression) {
            stackList.addLast(expression);
        }

        /**
         * 出棧
         * @return
         */
        public String pop() {
            return stackList.removeLast();
        }

        /**
         * 棧頂元素
         * @return
         */
        public String top() {
            return stackList.getLast();
        }

        /**
         * 棧是否爲空
         * @return
         */
        public boolean isEmpty() {
            return stackList.isEmpty();
        }
    }
}
發佈了57 篇原創文章 · 獲贊 12 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章