最近遇到一个需求,需要对逻辑表达式进行计算,支持数据类型包括数字,日期以及字符串,运算符包括<,<=,>,>=,(,),==,!=,&&,||。
代码结构:
OperatorEnum 运算符枚举类 枚举了支持的运算符,信息包括运算符及他们的优先级
OperandTypeEnum 数据类型枚举类
LogicUtil 逻辑运算工具类,实现逻辑运算
DateUtil 日期工具类,提供验证是否为日期以及比较大小方法,是否为日期类型见https://www.cnblogs.com/lin0/p/13323925.html
(后续还在实现逻辑表达式中添加了判断表达式是否合法代码,括号不匹配,运算符两边数据类型不一致,逻辑表达式不完整等,未粘贴上来)
OperatorEnum:
import java.util.HashMap;
import java.util.Map;
public enum OperatorEnum {
//!,&,|运算时暂时未考虑在内,主要用于判断字符是否为操作
NOT("!",0),
LT("<",1),
ELT("<=",1),
GT(">",1),
EGT(">=",1),
EQ("==",2),
NEQ("!=",2),
BAND("&", 3),
BOR("|", 4),
AND("&&",5),
OR("||",6),
E("=", 7);
private String name;
private Integer priority;
OperatorEnum(String name, Integer priority){
this.name = name;
this.priority = priority;
}
private static Map<String, OperatorEnum> enums = new HashMap<>();
public static void enumToMap(){
enums.put("!", OperatorEnum.NOT);
enums.put("<", OperatorEnum.LT);
enums.put("<=", OperatorEnum.ELT);
enums.put(">", OperatorEnum.GT);
enums.put(">=", OperatorEnum.EGT);
enums.put("==", OperatorEnum.EQ);
enums.put("!=", OperatorEnum.NEQ);
enums.put("&", OperatorEnum.BAND);
enums.put("|", OperatorEnum.BOR);
enums.put("&&", OperatorEnum.AND);
enums.put("||", OperatorEnum.OR);
enums.put("=", OperatorEnum.E);
}
public static OperatorEnum getEnumByName(String name){
if(enums.size() < 1){
enumToMap();
}
return enums.get(name);
}
public static boolean isOperator(String name){
if(enums.size() < 1){
enumToMap();
}
return enums.containsKey(name);
}
public Integer getPriority() {
return priority;
}
public String getName() {
return name;
}
}
OperandTypeEnum:
public enum OperandTypeEnum {
NUM("数字"),
DATE("日期"),
STR("字符串");
private String typeName;
OperandTypeEnum(String typeName){
this.typeName = typeName;
}
}
DateUtil:
/**
* @author Carol
* 比较日期大小
* @return
*/
public static int compareDate(String date1, String date2){
String d1 = date1.trim().replaceAll("-|:|/| ","");
String d2 = date2.trim().replaceAll("-|:|/| ", "");
StringBuilder sb1 = new StringBuilder(d1);
StringBuilder sb2 = new StringBuilder(d2);
while(sb1.length() < 14){
sb1.append("0");
}
while(sb2.length() < 14){
sb2.append("0");
}
long num1 = Long.parseLong(sb1.toString());
long num2 = Long.parseLong(sb2.toString());
return num1 == num2 ? 0 : num1 > num2 ? 1 : -1;
}
LogicUtil:
扫描二维码关注公众号,回复:
11724938 查看本文章
/**
* @author Carol
* 计算表达式
* 利用一个操作数栈和一个操作栈来进行计算
* 出栈情况:当前字符串为操作并且优先级小于栈顶操作元素或者遇到“)”,将操作栈中栈顶元素出栈,操作数栈中出栈俩元素
* 入栈情况:“(”直接入栈,或者当前操作优先级大于栈顶元素则直接入栈,操作数直接入栈
* @param expression
* @return
*/
public static boolean parse2(String expression) {
Deque<String> operands = new LinkedList<>();//操作数栈
Deque<String> operate = new LinkedList<>();//操作栈
StringBuilder sb = new StringBuilder();
try {
byte[] bytes = expression.getBytes("GBK");
for (int i = 0 ; i < bytes.length ; i ++) {
//汉字
if (bytes[i] < 0) {
if (i == bytes.length - 1) {//字符串异常截断
//不要这个字
} else {
sb.append(new String(new byte[]{bytes[i], bytes[i + 1]}, "GBK"));
i ++;
}
continue;
}
String thisOp = new String(new byte[]{bytes[i]});
//直接入栈
if ("(".equals(thisOp)) {
pushOperandIntoStack(operands, sb);
operate.addFirst(thisOp);
continue;
}
//到“(”之前的全部出栈
if (")".equals(thisOp)) {
pushOperandIntoStack(operands, sb);
String topOp = operate.pollFirst();
while(!"(".equals(topOp)){
calculate(operands, topOp);
if (operate.size() > 0) {
topOp = operate.pollFirst();
} else {
//括号没匹配上,逻辑表达式出错
return false;
}
}
continue;
}
if (OperatorEnum.isOperator(thisOp)) {//当前是否为操作符
//是,1.查看之前是否有字符未入栈,先入栈字符
pushOperandIntoStack(operands, sb);
//2.查看下一个是否为操作,并且非括号,是合并当前一起入操作栈
String nextOp = new String(new byte[]{bytes[i + 1]});
//下个与当前一起组成一个操作
if (!"(".equals(nextOp) && !")".equals(nextOp) && OperatorEnum.isOperator(nextOp)) {
thisOp += nextOp;
i ++;
}
//判断当前操作与栈顶操作优先级
if(operate.size() > 0){
String topOp = operate.getFirst();
while(!"(".equals(topOp) && OperatorEnum.getEnumByName(topOp).getPriority() < OperatorEnum.getEnumByName(thisOp).getPriority()){
//优先级高,出栈进行计算
topOp = operate.pollFirst();
calculate(operands, topOp);
if (operate.size() > 0) {
topOp = operate.getFirst();
} else {
break;
}
}
}
operate.addFirst(thisOp);
} else {
sb.append(thisOp);
}
}
} catch (Exception e){
e.printStackTrace();
}
if(sb.length() > 0){
operands.addFirst(sb.toString());
}
while(operate.size() > 0){
String topOp = operate.pollFirst();
calculate(operands, topOp);
}
if (operands.size() > 0){
String str = operands.pollFirst();
return StringUtils.isNotBlank(str) ? Boolean.parseBoolean(str) : false;
}
return false;
}
private static void pushOperandIntoStack(Deque operands, StringBuilder sb){
if(sb.length() > 0){
operands.addFirst(sb.toString());
sb.setLength(0);
}
}
private static void calculate(Deque<String> operands, String topOp){
String operand2 = operands.pollFirst().trim();
String operand1 = operands.pollFirst().trim();
//判断两个操作数类型,不一致不可比较直接返回false
OperandTypeEnum type1 = judgeType(operand1);
OperandTypeEnum type2 = judgeType(operand2);
if (type1 == type2) {
switch (type1){
case NUM:
operands.addFirst(numCalculate(Long.parseLong(operand1), Long.parseLong(operand2),
OperatorEnum.getEnumByName(topOp)) + "");
break;
case DATE:
operands.addFirst(dateCalculate(operand1, operand2, OperatorEnum.getEnumByName(topOp)) + "");
break;
case STR:
operands.addFirst(strCalculate(operand1, operand2, OperatorEnum.getEnumByName(topOp)) + "");
break;
}
} else {
operands.addFirst("false");
}
}
private static OperandTypeEnum judgeType (String operands) {
operands = operands.trim();
if (Pattern.matches("^[-\\+]?[\\d]*$", operands)) {
return OperandTypeEnum.NUM;
}
if (DateUtil.verifyDateLegal(operands)) {
return OperandTypeEnum.DATE;
}
return OperandTypeEnum.STR;
}
private static boolean numCalculate(long operand1, long operand2, OperatorEnum operate){
switch (operate){
case LT:
return operand1 < operand2;
case ELT:
return operand1 <= operand2;
case GT:
return operand1 > operand2;
case EGT:
return operand1 >= operand2;
case EQ:
return operand1 == operand2;
case NEQ:
return operand1 != operand2;
default:
return true;
}
}
private static boolean strCalculate(String operand1, String operand2, OperatorEnum operate){
switch (operate){
case EQ:
return operand1.equals(operand2);
case NEQ:
return !operand1.equals(operand2);
case AND:
return "true".equals(operand1) && "true".equals(operand2);
case OR:
return "true".equals(operand1) || "true".equals(operand2);
default:
return true;
}
}
private static boolean dateCalculate(String operand1, String operand2, OperatorEnum operate){
switch (operate){
case LT:
return DateUtil.compareDate(operand1, operand2) == -1 ? true : false;
case ELT:
return DateUtil.compareDate(operand1, operand2) <= 0 ? true : false;
case GT:
return DateUtil.compareDate(operand1, operand2) == 1 ? true : false;
case EGT:
return DateUtil.compareDate(operand1, operand2) >= 0 ? true : false;
case EQ:
return DateUtil.compareDate(operand1, operand2) == 0 ? true : false;
case NEQ:
return DateUtil.compareDate(operand1, operand2) != 0 ? true : false;
default:
return true;
}
}
测试代码:
System.out.println( LogicUtil.parse2("2==3 && 2==2 || 3==3"));
System.out.println("true == " + parse2("3>2"));
System.out.println("false == " + parse2("true == false"));
System.out.println("false == " + parse2("(3<2||2==2)&&(4==5)"));
System.out.println("false == " + parse2("(abc==fdg||民事==民事&&嘻嘻==嘻ha)&&(4>2||5<10)"));
System.out.println("true == " + parse2("()(((abc!=fdg||民事==民事&&嘻嘻==嘻ha)&&(4>2||5<10)))"));
System.out.println("true == " + parse2("5 < 19"));
System.out.println("false == " + parse2("5 > 19"));
测试结果: