论坛首页 Java企业应用论坛

java解析四则运算+-*/()字符串表达式,并按照精度进行计算结果,还提供按精度比较的方法

浏览 3553 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-03-29  

前言:最近涉及到业务人员配置数学表达式,要求解析并按照精度进行计算

 

参考了如下百度百科对逆波兰算法的讲解进行开发,开发过程中还参考了一篇精度算法的文章

1:http://baike.baidu.com/view/552648.htm

2:还有一遍文章忘了链接了,如果侵犯作者权利请告知,我立即删除

 

该类支持精度计算,支持负数,精度比较等功能。计算按照逆波兰方式解析。

 

/**
 * @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();
        }
    }
}

 

论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics