简单字符串表达式的计算

最近看了后缀表达式,之前也遇到过类似的面试题,遂写这篇博客,用于以后复用(其中表达式的校验并没有完成)。

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * 字符串表达式计算 
 *
 * 2018年2月26日上午11:18:08
 */
public class StringExpressEvaluateUtil {

	/**
	 * 符号优先级集合
	 * 该优先级别用于将中缀表达式转换成后缀表达式过程中使用
	 */
	final static Map<String,Integer> SIGNP_RIORITY =new HashMap<>(); 

	/**
	 * 成对符号集合
	 * 暂时仅用于在将中缀表达式转换成后缀表达式过程中,校验到")",往前匹配左括号。
	 */
	final static Map<String,Integer> SIGNP_PAIR =new HashMap<>();
	
	private StringExpressEvaluateUtil(){
		//不允许外部进行实例化
	}
	private static StringExpressEvaluateUtil instance;
	
	private static StringExpressEvaluateUtil getInstance(){
		if(instance == null) {
			return new StringExpressEvaluateUtil();
		}else {
			return instance;
		}
	}

	static {

		SIGNP_RIORITY.put("+", 1);
		SIGNP_RIORITY.put("-", 1);
		SIGNP_RIORITY.put("*", 2);
		SIGNP_RIORITY.put("/", 2);
		SIGNP_RIORITY.put("(", -1);
		
		SIGNP_PAIR.put("(",-1);
		SIGNP_PAIR.put(")", 1);
		
	}

	/**
	 * 
	 * @param srcExpress 原始表达式
	 * @return 后缀表达式,用于具体的计算使用
	 * @author riseSun
	
	 * 2018年2月26日上午11:19:59
	 */
	public String transformExpress(String srcExpress) {
		
		if(srcExpress == null) {
			throw new IllegalArgumentException("原始表达式不能为null");
		}

		StringBuilder postfixExpress = new StringBuilder();

		if("".equals(srcExpress.trim())) {
			return postfixExpress.toString();
		}

		//校验原始表达式是否有误
		checkSrcExpress(srcExpress);
		//符号栈 = 中缀->后缀中间产品
		Stack<String> signStack = new Stack<String>();

		/**
		 * 这里为了简单,所以假定原始表达式中,各个字符间具有空格,这样有助于表达式中数据的获取,如下表达式示例中,
		 * 要提取其中的数字30,以及10.2比较麻烦。
		 * 当然,如果没有空格,本人想到使用一下方法:
		 * {假设表达式为:-9+(30-1)*3+10.2/2 }
		 *	将其中的数字,用a、b、c、d、e...代替(记录代替的数字) ,这样可以使用toCharArray()或者split("");获取其中的字符。
		 *  获取到最终后缀表达式之后,将这些字母用原先的数字替换(这个替换过程可以在具体计算表达式的时候再做)
		 */
		for(String c: srcExpress.split(" ")) {
			switch (c) {
				case "+":
				case "-":
				case "*":
				case "/":
					operateSign(c,signStack,postfixExpress);
					break;
				case "(":
					//左括号直接压入到符号栈中
					signStack.push(c);
					break;
				case ")":
					//右括号单独处理
					operateParentheses(c,signStack,postfixExpress);
					break;
				default:
					//除符号外,其他的操作数直接追加到输出中
					postfixExpress.append(" "+c);
					break;
			}
		}
		//最后将符号栈中剩余的符号,追加到输出中
		while(!signStack.isEmpty()) {
			postfixExpress.append(" "+signStack.pop());
		}
		return postfixExpress.toString();
	}
	/**
	 * 校验字符串表达式的正确性
	 * @param srcExpress
	 * @author riseSun
	
	 * 2018年2月26日下午2:55:26
	 */
	public void checkSrcExpress(String srcExpress) {
//		if(false) {
//			throw new IllegalArgumentException("原始表达式存在错误");
//		}
	}
	/**
	 * 处理读取的符号
	 * @param newSign
	 * @param signStack
	 * @param postfixStack
	 * @author riseSun
	
	 * 2018年2月26日下午12:46:38
	 */
	public void operateSign(String newSign,Stack<String> signStack,StringBuilder postfixExpress) {
		//满足空栈或者优先级大于符号栈顶元素的优先级时,将符号直接压入栈中
		if(signStack.isEmpty() || (SIGNP_RIORITY.get(newSign) > SIGNP_RIORITY.get(signStack.peek()))) {
			signStack.push(newSign);
		}else {//出栈,直到栈顶元素优先级<=新符号,或者空栈
			//将符号打印到后缀栈中
			postfixExpress.append(" "+signStack.pop());
			operateSign(newSign, signStack, postfixExpress);
		}
	}
	/**
	 * 处理右括号
	 * 从符号栈中压出元素,并将元素压入到结果栈中,
	 * 直到符号栈中压出相反的符号为止
	 * @param newSign
	 * @param signStack
	 * @param postfixStack
	 * @author riseSun
	 * 2018年2月26日下午12:47:16
	 */
	public void operateParentheses(String newSign,Stack<String> signStack,StringBuilder postfixExpress) {
		while(!signStack.isEmpty() && !isPair(newSign,signStack.peek())) {
			postfixExpress.append(" "+signStack.pop());
		}
		//废弃栈中的左括号
		signStack.pop();
	}
	/**
	 * 判断两个符号是否是对称符号,例如(),[],{}..等
	 * 由SIGNP_PAIR来维护对称关系
	 * @param sign
	 * @param anotherSign
	 * @author riseSun
	
	 * 2018年2月26日下午12:53:30
	 */
	public boolean isPair(String sign,String anotherSign) {
		Integer signInt;
		Integer anotherSignInt;
		return (signInt = SIGNP_PAIR.get(sign)) !=null 
				&& (anotherSignInt = SIGNP_PAIR.get(anotherSign)) !=null 
				&& signInt.intValue()+anotherSignInt.intValue() == 0;
	}

	
	/**
	 * 根据后缀表达式进行计算
	 * @param postfixStack
	 * @return
	 * @author riseSun
	
	 * 2018年2月26日下午2:20:05
	 */
	public static double evaluate(String postfixStack) {

		//用于存放操作数以及中间计算结果
		Stack<String> evaluteStack = new Stack<>();

		String[] source = postfixStack.split(" ");

		for(String s : source) {
			double result;
			switch(s) {
				case "+":
					result = Double.parseDouble(evaluteStack.pop()) + Double.parseDouble(evaluteStack.pop());
					evaluteStack.push(result+"");
					break;
				case "-":
					double meiosis = Double.parseDouble(evaluteStack.pop());//减数
					double minuend = Double.parseDouble(evaluteStack.pop());//被减数
					result = minuend - meiosis;
					evaluteStack.push(result+"");
					break;
				case "*":
					result = Double.parseDouble(evaluteStack.pop()) * Double.parseDouble(evaluteStack.pop());
					evaluteStack.push(result+"");
					break;
				case "/":
					double divisor = Double.parseDouble(evaluteStack.pop());//除数
					double dividend = Double.parseDouble(evaluteStack.pop());//被除数
					result = dividend / divisor; 
					evaluteStack.push(result+"");
					break;
				default:
					//操作数直接压入栈中
					evaluteStack.push(s);
					break;
			}
		}
		//返回最终留在栈中的结果
		return Double.parseDouble(evaluteStack.pop());
	}

	/**
	 * 根据中缀表达式,直接计算结果
	 * @param midExpress
	 * @return
	 * @author riseSun
	
	 * 2018年2月26日下午3:02:28
	 */
	public static double evaluateMidExpress(String midExpress) {
		StringExpressEvaluateUtil instance = getInstance();
		return StringExpressEvaluateUtil.evaluate(instance.transformExpress(midExpress).trim());
	}
	
}

因各个项目中需要使用根据字符串计算数值,这里写出一个算法,专门计算字符串。配有大量常用公式。只有一个人方法,直接调用即可。 类名:CustomMath 函数名:Calculations(string value) 说明:求解算式表达式字符串的值 表达式中包含的符号或函数: truncate, ceiling,floor,round,log10, sign,sinh,sqrt, asin,atan,cosh, tanh, sin,cos,tan ,abs,acos, exp,log,max,min,pow,mod,+,-,*,/,',',(,) 函数说明:(不区分大小写) truncate(num) 计算指定数的整数部分 truncate(1.23)=1 ceiling (num) 返回大于或等于指定的双精度浮点数的最小整数值 ceiling(1.23)=2 floor(num) 返回小于或等于指定双精度浮点数的最大整数 floor(1.23)=1 round(num) 将双精度浮点值舍入为最接近的整数值 round(1.23)=1 round(num,num1) 将小数值按指定的小数位数舍入 round(1.23,1)=1.2 log10(num) 返回指定数字以 10 为底的对数 log10(10)=1 sign(num) 返回表示数字符号的值 sign(1.23)=1 sinh(num) 返回指定角度的双曲正弦值 sinh(1.23)=1.5644 sqrt(num) 返回指定数字的平方根 sqrt(9)=3 sqrt(num,num1) 返回指定数字的num1根 sqrt(27,3)=3 asin(num) 返回正弦值为指定数字的角度 asin(0.5)=PI/6 atan(num) 返回正切值为指定数字的角度 atan(1)=45 cosh(num) 返回指定角度的双曲余弦值 cosh(1.23)=1.8567 tanh(num) 返回指定角度的双曲正切值 tanh(1.23)=0.8425 sin(num) 返回指定角度的正弦值 sin(PI/6)=0.5 cos(num) 返回指定角度的余弦值 sin(PI/3)=0.5 tan(num) 返回指定角度的余切值 sin(PI/4)=1 abs(num) 返回数字的绝对值 abs(-12)=12 acos(num) 返回余弦值为指定数字的角度 acos(0.5)=PI/3 exp(num) 返回 e 的指定次幂 exp(1)=2.718 log(num) 返回指定数字的自然对数(底为 e) log(e)=1 log(num,num1) 返回指定数字在使用指定底时的对数 log(e,e)=1 max(num,um1) 返回最大值 max(1,2)=2 min(num,num1) 返回最小值 min(1,2)=1 pow(num,num1) 返回指定数字的指定次幂 pow(2,2)=4 mod(num,num1) 返回余数 mod(3,2)=1 常量: PI 值:3.14159265358979323846 E 值:2.7182818284590452354 YEAR 值:当前年份 MONTH 值:当前月份 DAY 值: 当前日 HOUR 值:当前时 MINUTE 值:当前分 SECOND 值:当前秒 RANDOM 值:一个随机数(0-1 之间) 实例 系统计算:1+2*3/4-0.5=2 函数计算:1+2*3/4-0.5=2 调用方式:CustomMath.Calculations("1+2*3/4-0.5") 系统计算:(1+2)*3/4-0.5=1.75 函数计算:(1+2)*3/4-0.5=1.75 调用方式:CustomMath.Calculations("(1+2)*3/4-0.5") 系统计算:(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6=1.20185042515466 公式计算:(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6=1.20185042515466 调用方式:CustomMath.Calculations("(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6") 系统计算:sin(pow(3,2)/4)+3.5-9*sqrt(81)=-76.7219268031121 函数计算:sin(pow(3,2)/4)+3.5-9*sqrt(81)=-76.7219268031121 调用方式:CustomMath.Calculations("sin(pow(3,2)/4)+3.5-9*sqrt(81)")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值