`

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

    博客分类:
  • JAVA
阅读更多

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

 

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

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版计算器支持四则混合运算.+-*/()

    总的来说,这个Java版计算器项目涵盖了基本的算术运算、数据类型处理、字符串解析、栈数据结构的应用以及异常处理等多个Java编程核心概念,是学习和巩固编程技能的一个好练习。通过实际编写和测试这个计算器,开发者...

    Java面试宝典.doc

    ### Java面试宝典知识点解析 #### 一、Java基础部分 **1.1 多个类在一个.java文件中** - **问题**: 在一个`.java`源文件中是否可以包括多个类(不是内部类)? - **限制**: 可以包含多个类,但必须满足以下条件: ...

    JAVA习题库#第四章-方法.doc

    ### JAVA习题库#第四章-方法知识点解析 #### 一、判断题知识点解析 **1. Java 语言的模块只有类。** - **答案:** 错误 - **解析:** 在Java中,除了类之外,还有接口(interface)、枚举(enum)等其他类型的模块。...

    表达式计算Java课设报告

    **测试**阶段,需要对各种复杂表达式进行测试,确保程序能够正确解析和计算,包括但不限于基本的四则运算、带有括号的嵌套表达式、以及各种数学函数的组合。 最后,在**总结**中,学生可能讨论了开发过程中的挑战、...

    JAVA经典教材笔记

    - 字符串操作方法:concat、substring、indexOf等。 - 比较字符串:equals与==的区别。 - **引用传递及其应用** - 引用传递的深入理解:传递的是对象地址而非副本。 - 应用场景:修改对象状态。 - **this关键字*...

    java算法练习题 大家下载看看啦

    ### Java算法练习题知识点解析 #### 1. 斐波那契数列 - **描述**:编写一个程序,计算斐波那契数列的前N项。 - **实现思路**: - 使用循环结构(如`for`循环)来依次计算每一项的值。 - 设置两个变量分别存储...

    Java经典面试题+答案(带书签)pdf

    - `&`是按位与操作符,用于比较两个表达式的值,并返回一个布尔结果。 - `&&`是逻辑与操作符,同样用于比较两个表达式的值,但在某些情况下可以避免不必要的计算(短路逻辑)。 **3. Byte类型的-128怎么表示** - ...

    2021-2022计算机二级等级考试试题及答案No.16733.docx

    - **解析**: 按 ESC 键并不是关闭数据库的标准方法。 ### 24. 逻辑型数据到整型数据转换 - **知识点**: 将逻辑型数据转换为整型数据时,True 通常转换为 -1,False 转换为 0。 - **解析**: 选项 D 正确描述了这种...

    2021-2022计算机二级等级考试试题及答案No.15484.docx

    - **解析**:表达式中的运算会按照特定的优先级顺序进行。在给出的例子中,`X + 1` 的计算优先级最高。因此,正确答案是 D。 ### 12. 数据处理的最小单位 - **知识点**:数据处理的基本单元。 - **解析**:数据...

    JAVA开发面试附答案

    Java中,当两个整数进行除法运算时,结果也是整数,即结果会被截断而不是四舍五入。而乘法运算则遵循标准的数学运算规则。 **选择题解析:** 正确答案是 **C**:5000。 - `5688 / 1000` 的结果是 `5`(整数除法),...

    google面试题(部分)

    大整数的四则运算 - **问题描述**:实现一个能够处理任意大小整数的加减乘除运算。 - **知识点**:大数算法,高精度计算。 #### 6. Hindex算法实现 - **问题描述**:实现 Hindex 算法。 - **知识点**:算法实现,H...

    Java试题-面试-笔试

    - **解析:** 这个题目主要考察字符串转换为数值类型的方法。 - `Double.parseDouble(s1)`将字符串`s1`转换为双精度浮点数`0.5`。 - `Integer.parseInt(s2)`将字符串`s2`转换为整数`12`。 - 最终输出`x + y`的结果...

    计算器的写法(四则运算)

    在计算机科学与编程领域,设计一个能够处理包含括号的复杂表达式进行四则运算的计算器是一个常见的任务,这不仅考验了算法设计的能力,还涉及到字符串处理、数据类型转换等多个方面。本文将基于给定的Java代码片段,...

    2021-2022计算机二级等级考试试题及答案No.11687.docx

    **知识点**:在编程语言中,经常需要进行字符串的拼接和转换操作。 - **chr(65)**:在许多编程语言中,chr函数用于将ASCII码转换为对应的字符,这里65对应的字符是“A”。 **结论**:执行命令?chr(65)+"K"的结果是...

    java基本概念试卷

    - **字符串操作**:`String`类提供了一系列方法来进行字符串处理。 - 获取长度:`length()`。 - 字符串比较:`equals()`用于比较内容是否相等;`equalsIgnoreCase()`用于忽略大小写比较。 - 大小写转换:`...

    2021-2022计算机二级等级考试试题及答案No.3803.docx

    字符串比较 **题目描述:** 结果为逻辑真的表达式是什么? **答案解析:** - **A. "ABC"$"AB"**:这是错误的。 - **B. "ABC"$"ABXCYZ"**:这是错误的。 - **C. "ABCYZ"$"BC"**:这是错误的。 - **D. "BC"$"CBAABC...

    C++PracticeC++PracticeC++PracticeC++Practice

    #### 四、编程题解析 **1. 编写程序,输出以下信息。** ```cpp #include using namespace std; int main() { cout , How are you?" ; cout , I am learning C++ Language." ; return 0; } ``` **2. 编写 C++ ...

Global site tag (gtag.js) - Google Analytics