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
上一篇: win10系统硬盘里面的文件夹图标出现黑色背景怎么解决?
下一篇: js 实现简易点击切换显示或隐藏