package com.yanzuoguang.util.helper; import com.yanzuoguang.util.YzgError; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 公式计算 * * @author 颜佐光 */ public class FormulaHelper { private static final String REGEX_DOUBLE = "^[-+]?[0-9]*\\.?[0-9]+$"; private static final String REGEX_QUOT = "(^.*?)\\((.+?)\\)(.*?$)"; private static final String REGEX_CALC_ADD_PLUS = "(^.*)([+\\-])(.*?$)"; private static final String REGEX_CALC_MULTIPLY_MOD = "(^.*)([*/])(.*?$)"; private static final String REGEX_CALC_TAG = "[+\\-*/()]+"; private static final String EMPTY_CHAR = " "; private static final String TEMP_VAR_NAME = "@temp"; private static FormulaHelper calcInstance = new FormulaHelper(); /** * 公式参数获取 */ public interface CalcParameter { /** * 获取参数值 * * @param parameterName 获取参数值的名称 * @return */ double getValue(String parameterName); } /** * 获取excel列序号 * * @param columnName * @return */ public static final int getExcelIndex(String columnName) { columnName = columnName.toLowerCase(); if (!columnName.matches("^[a-z]+$")) { throw YzgError.getRuntimeException("013", columnName); } // 从名称转换列序号 int formulaColumnIndex = 0; char[] chs = new char[columnName.length()]; columnName.getChars(0, columnName.length(), chs, 0); for (int i = 0; i < chs.length; i++) { formulaColumnIndex = formulaColumnIndex * 26 + (chs[i] - 'a' + 1); } formulaColumnIndex--; return formulaColumnIndex; } /** * 计算公式 * * @param formula 公式内容,支持括号、空格、数字、+、-、*、/、变量名,如: A * ( B + C ) * @return 运算后的结果 */ public static double calc(String formula) { return calcInstance.calRun(formula, StringHelper.EMPTY, null); } /** * 计算公式 * * @param formula 公式内容,支持括号、空格、数字、+、-、*、/、变量名,如: A * ( B + C ) * @param calcParameter 获取变量值 * @return 运算后的结果 */ public static double calc(String formula, CalcParameter calcParameter) { return calcInstance.calRun(formula, StringHelper.EMPTY, calcParameter); } /** * 计算公式 * * @param formula 公式内容,支持括号、空格、数字、+、-、*、/、变量名,如: A * ( B + C ) * @param tempName 临时变量名称 * @param calcParameter 获取变量值 * @return 运算后的结果 */ public static double calc(String formula, String tempName, CalcParameter calcParameter) { return calcInstance.calRun(formula, tempName, calcParameter); } /** * 计算公式 * * @param formula 公式内容,支持括号、空格、数字、+、-、*、/、变量名,如: A * ( B + C ) * @param tempName 临时变量名称 * @param calcParameter 获取变量值 * @return 运算后的结果 */ private double calRun(String formula, String tempName, CalcParameter calcParameter) { if (StringHelper.isEmpty(formula)) { return 0; } tempName = StringHelper.getFirst(tempName, TEMP_VAR_NAME); // 去掉公式空格 formula = formula.replaceAll(EMPTY_CHAR, ""); // 获取所有的变量名称 List<String> varNames = getVarNames(formula); // 获取所有变量值 Map<String, Double> varValues = getVarValues(varNames, calcParameter); // 返回计算结果 return calcProc(formula, tempName, varValues); } /** * 获取变量表 * * @param formula 输入等式的右边 **/ private List<String> getVarNames(String formula) { List<String> list = new ArrayList<>(); //清理所有运算符,并且包含多个运算符号时,将多个运算符号当成一个运算符号处理 String formulaTo = formula.replaceAll(REGEX_CALC_TAG, EMPTY_CHAR); String[] items = formulaTo.split(EMPTY_CHAR); for (String item : items) { // 判断是否是空字符串、纯数字,是则不属于变量 if (StringHelper.isEmpty(item) || item.matches(REGEX_DOUBLE)) { continue; } list.add(item); } return list; } /** * 获取所有变量值 * * @param varNames 变量名称列表 * @param calcParameter 获取变量值 * @return 变量值 */ private Map<String, Double> getVarValues(List<String> varNames, CalcParameter calcParameter) { // 获取所有的变量值 Map<String, Double> varValues = new HashMap<>(varNames.size()); for (String name : varNames) { if (varValues.containsKey(name) || calcParameter == null) { continue; } double value = calcParameter.getValue(name); varValues.put(name, value); } return varValues; } /** * 最终计算结果 * * @param formula 公式 * @param tempName 临时变量名称 * @param varValues 变量值 * @return */ private double calcProc(String formula, String tempName, Map<String, Double> varValues) { double ret = 0; if (formula.matches(REGEX_QUOT)) { // 获取第一个括号和最后一个括号 Matcher matcher = getMatcher(REGEX_QUOT, formula); double quotResult = calcProc(matcher.group(2), tempName, varValues); String mutualKey = TEMP_VAR_NAME + varValues.size(); varValues.put(mutualKey, quotResult); String formulaTo = matcher.group(1) + mutualKey + matcher.group(3); ret = calcProc(formulaTo, tempName, varValues); } else if (formula.matches(REGEX_CALC_ADD_PLUS)) { // 判断是否包含+-运算符号 Matcher matcher = getMatcher(REGEX_CALC_ADD_PLUS, formula); double leftResult = calcProc(matcher.group(1), tempName, varValues); double rightResult = calcProc(matcher.group(3), tempName, varValues); ret = calcItem(matcher.group(2), leftResult, rightResult); } else if (formula.matches(REGEX_CALC_MULTIPLY_MOD)) { // 判断是否包含*/运算符号 Matcher matcher = getMatcher(REGEX_CALC_MULTIPLY_MOD, formula); double leftResult = calcProc(matcher.group(1), tempName, varValues); double rightResult = calcProc(matcher.group(3), tempName, varValues); ret = calcItem(matcher.group(2), leftResult, rightResult); } else if (formula.matches(REGEX_DOUBLE)) { ret = StringHelper.toDouble(formula); } else { ret = StringHelper.toDouble(varValues.get(formula)); } // System.out.println("公式: " + formula + " 值" + ret); return ret; } /** * 匹配正则表达式和公式 * * @param regex 正则表达式 * @param formula 公式 * @return 匹配后的结果 */ private Matcher getMatcher(String regex, String formula) { Pattern p = Pattern.compile(regex); Matcher matcher = p.matcher(formula); if (!matcher.find()) { throw YzgError.getRuntimeException("014"); } return matcher; } private double calcItem(String flag, double a, double b) { switch (flag) { case "+": return CalcHelper.add(a, b); case "-": return CalcHelper.sub(a, b); case "*": return CalcHelper.mul(a, b); case "/": return CalcHelper.div(a, b); default: return 0; } } }