前言
背景:在一些情况下需要用户通过四则运算的方式自定义程序的运行规则,这时就需要对用户的输入表达式进行校验,具体实现是通过入栈,出栈解析表达式,来校验元素子表达式时候符合运算规则。
一、具体代码实现
代码示例如下:
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
/**
* 校验四则运算语法
* 目前只支持小括号级别的优先级运算"()"
* 在这个例子中 支持 “且 或” 运算符 ,如需要添加其他运算符 可以通过setLogicList(List<String> logicList)进行赋值
* node:这个类并没有经过完全的测试,请不要完全信任这个类
* 该类的核心算法参考网上教程:https://zhuanlan.zhihu.com/p/273245625
*/
public class FourArithmeticOperationsValidate {
/**
* 代表临时运算结果,没有特殊含义
*/
private String tempResult = "TR";
private List<String> logicList = Arrays.asList("且", "或");
private List<String> operatorList = Arrays.asList("且", "或", "(", ")");
/**
* 校验表达式
*
* @param exp
* @return
*/
public boolean doValidate(String exp) {
// 定义一个栈,用来存储运算过程
Stack<String> stack = new Stack<>();
// 第一步去除括号,即先计算括号里面的
for (int i = 0; i < exp.length(); i++) {
String character = String.valueOf(exp.charAt(i));
if (character.equals(" ")) {
continue;
}
if (character.equals(")")) {
// 如果遇到右括号,把栈中的数据一个一个取出来放入另外一个栈中,直到遇见左括号
Stack<String> sb = new Stack<>();
while (!stack.isEmpty() && !stack.peek().equals("(")) {
sb.push(stack.pop());
}
//如果不存在 “(” 说明 括号没有闭合
if (stack.size() ==0 || !"(".equals(stack.peek())) return false;
stack.pop();// 删除栈中的左括号
if (!validationExpression(sb)) {
return false; //如果校验结果为false,无需进行继续校验
}
stack.push(tempResult);// 将括号里面的内容计算之后重新放入栈中
} else {
// 没有遇到右括号
if (stack.isEmpty()) {
stack.push(character);
continue;
}
if (operatorList.contains(character)) {
stack.push(character); // 如果是运算符或者左括号,直接入栈
} else {
// 如果不是操作符,需要和之前入栈的数字拼接
if (operatorList.contains(stack.peek()) || tempResult.equals(stack.peek())) {
stack.push(character); // 如果上一个入栈的是操作符,直接入栈
} else {
// 如果上一个入栈的不是操作符,先出栈,再拼接,最后入栈
stack.push(stack.pop() + character);
}
}
}
}
// 计算最后的结果
if (stack.size() == 1) {// 计算完括号里面的,如果栈中只剩下一个元素
return validationExpression(stack);
} else {// 计算完括号里面的,栈中还有多个元素,继续计算
// 但是此时栈中的元素是正序排列的,需要变成倒序再计算
Stack<String> last = new Stack<>();
while (!stack.isEmpty()) {
last.push(stack.pop());
}
return validationExpression(last);
}
}
/**
* 校验单个表达式是否合法
*
* @param exp 表达式 此时的表达式中不应该还有(,)
* @return true 校验通过 false 校验不通过
*/
private boolean validationExpression(Stack<String> exp) {
int size = exp.size();
if (size == 0 || size == 2 || (size == 1 && logicList.contains(exp.peek()))) return false;
if (exp.contains("(") || exp.contains(")")) return false;
boolean dataFlag = false; //当前字符串为数值
boolean optFlag = false;//当前字符串为操作符
for (int i = 0; i < exp.size(); i++) {
String s = exp.get(i);
if (!logicList.contains(s)) {
if (dataFlag) return false;
dataFlag = true;
optFlag = false;
} else {
if (optFlag) return false;
optFlag = true;
dataFlag = false;
}
}
return dataFlag;
}
public List<String> getLogicList() {
return logicList;
}
public void setLogicList(List<String> logicList) {
this.logicList = logicList;
}
public List<String> getOperatorList() {
return operatorList;
}
public void setOperatorList(List<String> operatorList) {
this.operatorList = operatorList;
}
}
以上代码的 validationExpression(Stack<String> exp) 方法主要用来验证子表达式是够符合运算规则。上面代码中的运算符是“且” “或” ,用户可以根据自已的需要通过setLogicList(List<String> logicList)方法来自定义运算符。
二、测试用例
示例代码如下:
public class FourArithmeticValidateTest {
public static void main(String[] args) {
FourArithmeticOperationsValidate expressTest = new FourArithmeticOperationsValidate();
System.out.println("1且(" +expressTest.doValidate("1且("));
System.out.println("1且()" +expressTest.doValidate("1且()"));
System.out.println(")" +expressTest.doValidate(")"));
}
}
总结
以上代码没有经过完全的测试,请不要直接cv,另外以上代码核心算法参考网上大佬教程,原文链接https://zhuanlan.zhihu.com/p/273245625