欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java工具类之公式计算器【版本1.0】

程序员文章站 2022-03-21 22:24:59
得到公式;处理所有的全角和半角空格,并且把中文括号转成英文括号;表达式操作符与值拆解;将中缀表达式转换为前缀表达式;前缀表达式计算...
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;

/**
 * 公式计算器
 * 
 * @author zql
 * 
 */
public class FormulaCalculator {
	
	public static void main(String args[]){
		double res = 128 - 9 / (60 + 4) * 64 - (6 + 9) / 15;
		System.out.println(res);
		FormulaCalculator sss = new FormulaCalculator("128-9/(60+4)*64-(6+9)/15");
		String result = sss.getResultForString(new ValGetterInterface() {
			public BigDecimal getVal(String code) {
				return new BigDecimal(code);
			}
		});
		System.out.println(result);
		sss = new FormulaCalculator("(1+1)+3*5-6*(5+5)/6");
		String result1 = sss.getResultForString(new ValGetterInterface() {

			public BigDecimal getVal(String code) {
				return new BigDecimal(code);
			}

		});
		System.out.println(result1);
	}
	
	private String zero = "0";
	/**
	 * 公式
	 */
	private String formula;
	/**
	 * 数据范围
	 */
	private int scale=8;
	private Queue<String> PrefixExpreQueue;
	private Set<String> depCode;
	
	public FormulaCalculator(String expr) {
		this.formula = toDealWith(expr);
		this.PrefixExpreQueue = toPrefixStack(parse(toDealWith(expr)));
	}
	
	public FormulaCalculator(String expr, int scale) {
		this.formula = toDealWith(expr);
		this.scale = scale;
		this.PrefixExpreQueue = toPrefixStack(parse(toDealWith(expr)));
	}
	
	/**
	 * 得到公式
	 * 
	 * @return
	 */
	public String getFormula() {
		return this.formula;
	}
	
	public String getResultForString(ValGetterInterface valGetter) {
		Queue<String> copyQueue = new LinkedList<String>();
		// 复制一个队列
		for (String item : PrefixExpreQueue) {
			copyQueue.offer(item);
		}
		return this.prefixQueueCal(copyQueue, valGetter);
	}
	
	/**
	 * 处理所有的全角和半角空格,并且把中文括号转成英文括号
	 * 
	 * @param ori 要处理的字符串
	 * @return
	 */
	private String toDealWith(String ori) {
		// 替换所有半角空格和替换所有全角空格,即输入法为中文状态下打的空格
		return ori.replaceAll("\\s+", "").replaceAll("\\p{Zs}", "").replaceAll("(", SignEnum.LEFT_BRACKETS.getSign()).replaceAll(")", SignEnum.RIGHT_BRACKETS.getSign());
	}
	
	/**
	 * 表达式操作符与值拆解
	 */
	private List<String> parse(String expression) {
		depCode = new HashSet<String>();
		String[] exprs = expression.split("");
		List<String> result = new ArrayList<String>();
		StringBuilder sb = new StringBuilder();
		for (String c : exprs) {
			if (SignEnum.isContain(c)) {
				String str = sb.toString();
				if (str.length() > 0) {
					String code = sb.toString();
					result.add(code);
					depCode.add(code);
					sb = new StringBuilder();
				}
				result.add(c);
			} else {
				sb.append(c);
			}
		}
		String str = sb.toString();
		if (str.length() > 0) {
			result.add(str);
		}
		// 第一个字符为操作符+-*/的,再最前面补0修正表达式,例如 -34*2 修正为 0-34*2
		String firstItem = result.get(0);
		if (result.size() > 0 && (SignEnum.ADD.getSign().equals(firstItem) || SignEnum.SUBTRACT.getSign().equals(firstItem) 
				|| SignEnum.MULTIPLY.getSign().equals(firstItem) || SignEnum.DIVIDE.getSign().equals(firstItem))) {
			result.add(0, zero);
		}
		String lastItem = result.get(result.size() - 1);
		// 最后一个字符为操作符的,直接将表达式视为无效表达式,返回结果值0,例如 34*2*
		if (result.size() > 0 && (SignEnum.ADD.getSign().equals(lastItem) || SignEnum.SUBTRACT.getSign().equals(lastItem) 
				|| SignEnum.MULTIPLY.getSign().equals(lastItem) || SignEnum.DIVIDE.getSign().equals(lastItem))) {
			result = new ArrayList<String>(1);
			result.add(zero);
		} else {
			depCode.add(lastItem);
		}
		return result;
	}
	
	/**
	 * 将中缀表达式转换为前缀表达式,遵循以下步骤:
	 * <br/>(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
	 * <br/>(2) 从右至左扫描中缀表达式;
	 * <br/>(3) 遇到操作数时,将其压入S2;
	 * <br/>(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
	 * <br/>(4-1) 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈;
	 * <br/>(4-2) 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入S1;
	 * <br/>(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
	 * <br/>(5) 遇到括号时:
	 * <br/>(5-1) 如果是右括号“)”,则直接压入S1;
	 * <br/>(5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃;
	 * <br/>(6) 重复步骤(2)至(5),直到表达式的最左边;
	 * <br/>(7) 将S1中剩余的运算符依次弹出并压入S2;
	 * <br/>(8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
	 * 
	 * @param items
	 * @return
	 */
	private Queue<String> toPrefixStack(List<String> items) {
		Stack<String> s1 = new Stack<String>();
		Stack<String> s2 = new Stack<String>();
		Collections.reverse(items);
		for (String item : items) {
			if (SignEnum.isContain(item)) {
				if (SignEnum.RIGHT_BRACKETS.getSign().equals(item)) {
					s1.push(item);
				} else if (SignEnum.LEFT_BRACKETS.getSign().equals(item)) {
					while (true) {
						// 如果没有闭合括号,则表示表达式错误,不做计算,直接返回只包含了0的队列
						if (s1.isEmpty()) {
							Queue<String> zeroQueue = new LinkedList<String>();
							zeroQueue.offer(zero);
							return zeroQueue;
						}
						String prevOp = s1.pop();
						if (SignEnum.RIGHT_BRACKETS.getSign().equals(prevOp)) {
							break;
						}
						s2.push(prevOp);
					}
				} else {
					while (true) {
						String prevOp = null;
						if (!s1.isEmpty()) {
							prevOp = s1.lastElement();
						}
						if (Objects.isNull(prevOp) || SignEnum.RIGHT_BRACKETS.getSign().equals(prevOp)) {
							s1.push(item);
							break;
						} else {
							if (CalOperatorGenerator.getOp(item).getPriority() >= CalOperatorGenerator.getOp(prevOp).getPriority()) {
								s1.push(item);
								break;
							} else {
								s1.pop();
								s2.push(prevOp);
							}
						}
					}
				}
			} else {
				s2.push(item);
			}
		}
		while (!s1.isEmpty()) {
			s2.push(s1.pop());
		}
		// 转换成队列
		Queue<String> exprsQueue = new LinkedList<String>();
		while (!s2.isEmpty()) {
			exprsQueue.offer(s2.pop());
		}

		return exprsQueue;
	}
	
	/**
	 * 前缀表达式计算(从左到右遍历前缀表达式,遇到操作符,放进栈,遇到操作数,查看栈顶,栈顶为操作符,放进栈,栈顶为操作数,取出栈顶操作数和操作符,进行运算,运算后继续判断栈顶的情况。)
	 * 
	 * @param ps 保存着前缀表达式的队列,队列内元素对应表达式顺序为:队列头->队列尾 , 前缀表达式左->右
	 * @param valGetter
	 * @return
	 */
	private String prefixQueueCal(Queue<String> ps, ValGetterInterface valGetter) {
		Stack<String> s1 = new Stack<String>();
		String item = null;
		while (!ps.isEmpty()) {
			item = ps.poll();
			CalOperatorInterface op = CalOperatorGenerator.getOp(item);
			if (Objects.isNull(op)) {
				while (true) {
					if (s1.isEmpty() || Objects.nonNull(CalOperatorGenerator.getOp(s1.lastElement()))) {
						s1.push(item);
						break;
					} else {
						String valItem = s1.pop();
						String opItem = s1.pop();
						op = CalOperatorGenerator.getOp(opItem);
						BigDecimal theVal = op.calOperator(valGetter.getVal(valItem), valGetter.getVal(item), this.scale, BigDecimal.ROUND_HALF_UP);
						item = String.valueOf(theVal);
					}
				}
			} else {
				s1.push(item);
			}
		}
		return valGetter.getVal(item).toString();
	}
	
	/**
	 * @Description:值获取接口定义
	 * @Author:zql
	 *
	 */
	public interface ValGetterInterface {
		BigDecimal getVal(String code);
	}
	
	/**
	 * @Description:计算运算符接口
	 * @Author:zql
	 *
	 */
	private interface CalOperatorInterface {
		
		BigDecimal calOperator(BigDecimal val, BigDecimal divisor, int scale, int roundingMode);

		int getPriority();
	}
	
	/**
	 * @Description:计算方式生产类
	 * @Author:zql
	 *
	 */
	private static class CalOperatorGenerator {
		/**
		 * 加
		 */
		public static CalOperatorInterface add = new CalOperatorInterface() {
			public BigDecimal calOperator(BigDecimal val, BigDecimal val2, int scale, int roundingMode) {

				return val.add(val2);
			}

			public int getPriority() {
				return 1;
			}
		};
		/**
		 * 减
		 */
		public static CalOperatorInterface subtract = new CalOperatorInterface() {
			public BigDecimal calOperator(BigDecimal val, BigDecimal val2, int scale, int roundingMode) {
				return val.subtract(val2);
			}

			public int getPriority() {
				return 1;
			}
		};
		/**
		 * 乘
		 */
		public static CalOperatorInterface multiply = new CalOperatorInterface() {
			public BigDecimal calOperator(BigDecimal val, BigDecimal val2, int scale, int roundingMode) {
				return val.multiply(val2);
			}

			public int getPriority() {
				return 2;
			}
		};
		/**
		 * 除
		 */
		public static CalOperatorInterface divide = new CalOperatorInterface() {
			public BigDecimal calOperator(BigDecimal val, BigDecimal divisor, int scale, int roundingMode) {
				// 除数等于0的时候,计算无意义,为了防止异常,直接返回0作为结果
				if (BigDecimal.ZERO.compareTo(divisor) == 0) {
					return BigDecimal.ZERO;
				}
				return val.divide(divisor, scale, roundingMode);
			}

			public int getPriority() {
				return 2;
			}
		};
		
		/**
		 * @Description:根据相应的操作符获取相应的计算方式
		 * @Author:zql
		 *
		 * @param operatorName 操作符 + 、 - 、 * 、\
		 * @return
		 */
		public static CalOperatorInterface getOp(String operatorName) {
			if (SignEnum.ADD.getSign().equals(operatorName)) {
				return add;
			} else if (SignEnum.SUBTRACT.getSign().equals(operatorName)) {
				return subtract;
			} else if (SignEnum.MULTIPLY.getSign().equals(operatorName)) {
				return multiply;
			} else if (SignEnum.DIVIDE.getSign().equals(operatorName)) {
				return divide;
			}
			return null;
		}
	}

	public Set<String> getDepCode() {
		return depCode;
	}
	

	/**
	 * 计算所用到的符号
	 */
	enum SignEnum {
		/**加号“+”*/
		ADD("+"),
		/**减号“-”*/
		SUBTRACT("-"),
		/**乘号“*”*/
		MULTIPLY("*"),
		/**除号“/”*/
		DIVIDE("/"),
		/**英文左括号“(”*/
		LEFT_BRACKETS("("),
		/**英文左括号“)”*/
		RIGHT_BRACKETS(")");
		
		private String signType;
		
		private SignEnum(String signType) {
			this.signType = signType;
		}
		
		public String getSign() {
			return this.signType;
		}
		
		/**
		 * @Description:判断是否存在操作符
		 * @Author:zql
		 *
		 * @param sign
		 * @return
		 */
		public static boolean isContain(String sign) {
			SignEnum[] signEnums = SignEnum.values();
			for (SignEnum s : signEnums) {
				if (s.getSign().equals(sign)) {
					return true;
				}
			}
			return false;
		}
	}
	
}

本文地址:https://blog.csdn.net/mr_zql/article/details/110205138