目录
问题
实际项目中需要对接口的返回结果做处理,按照配置中的返回结果字段和算术运算符(+ - * /)和左右括号组合得到一个算术表达式,并计算得到值返回。
如何解决
- 获取到接口的返回值后,便可以得到一个具体的中缀表达式
- 将中缀表达式转化成后缀表达式(逆波兰式)
- 计算后缀表达式的值
代码
操作符枚举类
public enum Operator {
ADD(30, '+'),
SUB(30, '-'),
MUL(40, '*'),
DIV(40, '/'),
LEFT(0, '('),
RIGHT(0, ')');
static {
map = Arrays.stream(values()).collect(toMap(ExprUtils.Operator::getSymbol, e -> e));
}
private final int priority;
private final char symbol;
private static final Map<Character, ExprUtils.Operator> map;
Operator(Integer priority, Character symbol) {
this.priority = priority;
this.symbol = symbol;
}
public Character getSymbol() {
return this.symbol;
}
//返回对应的优先级数字
public static int getPriority(Character symbol) {
ExprUtils.Operator operator = map.get(symbol);
assert Objects.nonNull(operator);
return operator.priority;
}
public static boolean notRightBracket(Character symbol) {
ExprUtils.Operator operator = map.get(symbol);
if (Objects.isNull(operator)) {
return false;
}
return !operator.equals(RIGHT);
}
public static boolean isOperator(Character symbol) {
ExprUtils.Operator operator = map.get(symbol);
if (Objects.isNull(operator)) {
return false;
}
switch (operator) {
case ADD:
case SUB:
case MUL:
case DIV: return true;
default: return false;
}
}
}
表达式工具类
public class ExprUtils {
/*判断中缀表达式是否合法*/
public static boolean expressionCheck(String str) {
Deque<Character> stack = new LinkedList<>();
//不合法情况1:括号不匹配
for(char ch : str.toCharArray()) {
if(ch == '(') {
stack.push(ch);
}
if(ch == ')') {
if(stack.isEmpty()) {
return false;
}
stack.pop();
}
}
if (!stack.isEmpty()) {
return false;
}
//不合法情况2:操作符左右元素不合法
for(int i = 0 ; i < str.length() ; i++) {
//先判断 '-' 是不是负数符号
if('-' == str.charAt(i)) {
if( 0 == i || Operator.notRightBracket(str.charAt(i - 1))) //如果 - 在第一位或者前面有+-*/(,一定是作为负数符号而非操作符
i++;
}
if(Operator.isOperator(str.charAt(i))) {
if( 0 == i || str.length() - 1 == i)
return false;
if( Operator.isOperator(str.charAt(i - 1)) || Operator.isOperator(str.charAt(i + 1)) )
return false;
if( '(' == str.charAt(i - 1) || ')' == str.charAt(i + 1) )
return false;
}
}
//不合法情况3:左括号和右括号相邻,例:a+()和(a+b)(a+b)
for(int i = 0 ; i < str.length() ; i++) {
if(str.charAt(i) =='(' && i + 1 < str.length() && str.charAt(i + 1) == ')')
return false;
if(str.charAt(i) == ')' && i + 1 < str.length() && str.charAt(i + 1) == '(')
return false;
}
return true;
}
/**
* 将中缀表达式转化为后缀表达式
*
* @param exp 中缀表达式
* @return 后缀表达式
*/
public static List<String> parseExpression(String exp) {
Deque<Character> operation = new LinkedList<>();
List<String> target = new ArrayList<>();
for (int i = 0; i < exp.length(); i++) {
if(((exp.charAt(i) == '-' || exp.charAt(i) == '+') && (i == 0 || Operator.notRightBracket(exp.charAt(i - 1))))
|| Character.isDigit(exp.charAt(i)) ) {
// 如果是正号就不用加入,负号或者数字本身都要加入
StringBuilder tempStr = new StringBuilder(exp.charAt(i) != '+' ? exp.substring(i, i + 1) : "");
while (i + 1 < exp.length() && Character.isDigit(exp.charAt(i + 1))) {
tempStr.append(exp.charAt(++i));
}
target.add(tempStr.toString());
} else {
if ('(' == exp.charAt(i)) {
operation.push(exp.charAt(i));
} else if (')' == exp.charAt(i)) {
while ('(' != operation.peek()) {
target.add(operation.pop().toString());
}
operation.pop();
} else {
while (!operation.isEmpty()
&& Operator.getPriority(operation.peek()) >= Operator.getPriority(exp.charAt(i))) {
target.add(operation.pop().toString());
}
operation.push(exp.charAt(i));
}
}
}
while (!operation.isEmpty()) {
target.add(operation.pop().toString());
}
return target;
}
/**
* 计算后缀表达式
*
* @param list 后缀表达式
* @return 计算结果
*/
public static int calculate(List<String> list) {
Stack<Integer> stack = new Stack<>();// 创建栈
for (String item : list) {
if (item.matches("^-?\\d+(\\.\\d+)?$")) { //使用正则表达式匹配多位数
stack.push(Integer.parseInt(item));
} else {
int num2 = stack.pop(); // pop出两个数,并运算, 再入栈
int num1 = stack.pop();
int res;
switch (item) {
case "+": res = num1 + num2; break;
case "-": res = num1 - num2; break;
case "*": res = num1 * num2; break;
case "/": res = num1 / num2; break;
default: throw new RuntimeException("运算符有误");
}
stack.push(res);
}
}
return stack.pop();
}
}
测试类
class ExprUtilsTest {
@Test
void calculate() {
String infixExpression = "-6+((-2)+(2+4))";
if (!ExprUtils.expressionCheck(infixExpression)) {
System.out.println("表达式不合法");
} else {
System.out.println("中缀表达式为:" + infixExpression);
List<String> calculateExpression = ExprUtils.parseExpression(infixExpression);
System.out.println("后缀表达式为:" + calculateExpression);
System.out.printf("Answer=%d", ExprUtils.calculate(calculateExpression));
}
}
}