做excel单元格自定义校验规则时搞的一个小轮子,适合给不懂编码的人使用,如果会一些编码知识,直接使用groovy解析会更灵活。
可稍作拆解为计算器。
package com.weilink.commontools.utils.calculator;
import java.math.BigDecimal;
public class Mycalculator {
public static void main(String[] args) throws Exception {
System.out.println(explainExpress("MIN(3*4/6+MAX(3,4),2)=2"));
}
// 长的放在数组前面,因为在匹配表达式时优先匹配长的
private static String[] validSymbol = new String[]{">=", "<=", "!=", "≥", "≤", "≠", ">", "<", "="};
public static boolean explainExpress(String str) throws Exception {
return explainExpress(str, null).equals("true");
}
private static String explainExpress(String str, Function function) throws Exception {
str = str.replaceAll(" ", "");
if (null != function) {
return function.cal();
}
// 函数优先计算
str = functionCal(str);
// 递归计算
// 括号优先
if (str.contains(")")) {
int lIndex = str.lastIndexOf("(");
int rIndex = str.indexOf(")", lIndex);
return explainExpress(str.substring(0, lIndex) + explainExpress(str.substring(lIndex + 1, rIndex), null) + str.substring(rIndex + 1), null);
}
if (str.contains("|")) {
int index = str.lastIndexOf("|");
String valLeft = explainExpress(str.substring(0, index), null);
String valRight = explainExpress(str.substring(index + 1), null);
if (valLeft.equals("true") || valRight.equals("true")) {
return "true";
} else {
return "false";
}
}
if (str.contains("&")) {
int index = str.lastIndexOf("&");
String valLeft = explainExpress(str.substring(0, index), null);
String valRight = explainExpress(str.substring(index + 1), null);
if (valLeft.equals("true") && valRight.equals("true")) {
return "true";
} else {
return "false";
}
}
return expressValid(str);
}
private static String expressValid(String exp) throws Exception {
if (exp.equals("true")) {
return "true";
} else if (exp.equals("false")) {
return "false";
}
String symbol = null;
String[] formulas = null;
for (String sy : validSymbol) {
if (exp.split(sy).length == 2) {
symbol = sy;
formulas = exp.split(sy);
break;
}
}
if (formulas == null) {
return getResult(exp).toPlainString();
}
BigDecimal valueLeft = getResult(formulas[0]);
BigDecimal valueRight = getResult(formulas[1]);
switch (symbol) {
case ">":
return valueLeft.compareTo(valueRight) > 0 ? "true" : "false";
case "<":
return valueLeft.compareTo(valueRight) < 0 ? "true" : "false";
case "=":
// 默认等于的精度为四位
return valueLeft.subtract(valueRight).abs().compareTo(new BigDecimal("0.0001")) < 0 ? "true" : "false";
case "≥":
return valueLeft.compareTo(valueRight) >= 0 ? "true" : "false";
case ">=":
return valueLeft.compareTo(valueRight) >= 0 ? "true" : "false";
case "≤":
return valueLeft.compareTo(valueRight) <= 0 ? "true" : "false";
case "<=":
return valueLeft.compareTo(valueRight) <= 0 ? "true" : "false";
case "≠":
return valueLeft.compareTo(valueRight) != 0 ? "true" : "false";
case "!=":
return valueLeft.compareTo(valueRight) != 0 ? "true" : "false";
}
throw new Exception("错误的表达式");
}
private static String functionCal(String str) throws Exception {
// 这里设计的有问题,应该把所有函数设置为static数组变量放到Function中,判断最右侧变量的逻辑也
// 该相应改造
while (str.contains("MAX") || str.contains("MIN")) {
Function function;
int start = 0;
int startMin = str.indexOf("(", str.lastIndexOf("MIN"));
int startMax = str.indexOf("(", str.lastIndexOf("MAX"));
if (str.lastIndexOf("MIN") < 0) {
start = startMax;
function = new Max();
} else if (str.lastIndexOf("MAX") < 0) {
start = startMin;
function = new Min();
} else {
if (startMax > startMin) {
start = startMax;
function = new Max();
} else {
start = startMin;
function = new Min();
}
}
int end = getEnd(str, start);
String innerExpr = str.substring(start + 1, end);
function.setExpr(innerExpr);
String startStr = str.substring(0, start - 3);
String endStr = str.substring(end + 1);
str = startStr + explainExpress(str, function) + endStr;
}
return str;
}
private static int getEnd(String str, int start) throws Exception {
int count = 1;
for (int i = start + 1; i < str.length(); i++) {
if (str.charAt(i) == '(') {
count++;
}
if (str.charAt(i) == ')') {
count--;
}
if (count == 0) {
return i;
}
}
throw new Exception("错误的表达式");
}
public static BigDecimal getResult(String str) throws Exception {
if (str.isEmpty() || isNumber(str)) {
if (str.isEmpty()) {
return BigDecimal.ZERO;
}
if (str.endsWith("%")) {
str = str.replace("%", "");
return new BigDecimal(str).divide(new BigDecimal("100"));
}
return new BigDecimal(str);
}
if (str.contains(")")) {
int lIndex = str.lastIndexOf("(");
int rIndex = str.indexOf(")", lIndex);
return getResult(str.substring(0, lIndex) + getResult(str.substring(lIndex + 1, rIndex)) + str.substring(rIndex + 1));
}
//先乘除后加减
//乘除运算必须从左边算起
if (str.contains("*") || str.contains("/")) {
int locate;
int locateM = str.indexOf("*");
int loacateD = str.indexOf("/");
int type = 1;
if (loacateD < 0) {
locate = locateM;
} else if (locateM < 0) {
locate = loacateD;
type = 2;
} else {
if (locateM < loacateD) {
locate = locateM;
} else {
locate = loacateD;
type = 2;
}
}
int leftNum = getLeftNum(str, locate);
int rightNum = getRightNum(str, locate);
BigDecimal leftValue = new BigDecimal(str.substring(leftNum, locate));
BigDecimal rightValue;
String leftStr = str.substring(0, leftNum);
String rightStr;
if (rightNum >= str.length()) {
rightValue = new BigDecimal(str.substring(locate + 1));
rightStr = "";
} else {
rightStr = str.substring(rightNum + 1);
rightValue = new BigDecimal(str.substring(locate + 1, rightNum + 1));
}
String value;
if (type == 1) {
value = leftValue.multiply(rightValue).toPlainString();
} else {
value = leftValue.divide(rightValue, 10, BigDecimal.ROUND_DOWN).toPlainString();
}
return getResult(leftStr + value + rightStr);
}
if (str.contains("+")) {
int index = str.lastIndexOf("+");
return getResult(str.substring(0, index)).add(getResult(str.substring(index + 1)));
}
if (str.contains("-")) {
int index = str.lastIndexOf("-");
return getResult(str.substring(0, index)).subtract(getResult(str.substring(index + 1)));
}
throw new Exception("错误的表达式");
}
private static int getLeftNum(String str, int locate) {
for (int i = locate - 1; i >= 0; i--) {
if (!Character.isDigit(str.charAt(i)) && str.charAt(i) != '.' && str.charAt(i) != '%') {
return i + 1;
}
}
return 0;
}
private static int getRightNum(String str, int locate) {
for (int i = locate + 1; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i)) && str.charAt(i) != '.' && str.charAt(i) != '%') {
return i - 1;
}
}
return str.length();
}
static boolean isNumber(String str) {
for (int i = 0; i < str.length(); i++) {
if (!Character.isDigit(str.charAt(i)) && str.charAt(i) != '.' && str.charAt(i) != '%') {
return false;
}
}
return true;
}
}
几个扩展类
package com.weilink.commontools.utils.calculator;
public abstract class Function {
protected String expr;
public Function(String expr) {
this.expr = expr;
}
public Function() {
}
public abstract String cal()throws Exception;
public String getExpr() {
return expr;
}
public void setExpr(String expr) {
this.expr = expr;
}
}
package com.weilink.commontools.utils.calculator;
import java.math.BigDecimal;
public class Max extends Function {
@Override
public String cal() throws Exception{
BigDecimal val = null;
for (String str : expr.split(",")) {
BigDecimal current = Mycalculator.getResult(str);
if (val == null || val.compareTo(current) < 0) {
val = current;
}
}
return val.toPlainString();
}
}
package com.weilink.commontools.utils.calculator;
import java.math.BigDecimal;
public class Min extends Function {
@Override
public String cal() throws Exception{
BigDecimal val = null;
for (String str : expr.split(",")) {
BigDecimal current = Mycalculator.getResult(str);
if (val == null || val.compareTo(current) > 0) {
val = current;
}
}
return val.toPlainString();
}
}



