今天由于业务需要需要写一个通用的将solr查询表达式转换为特定结构下的sql表达式。由于数据结构已经遗忘好久了。所以写了一个处理四则表达式的demo,来为后续的通用的转换做一个参考。后续会将solr转化为sql的代码贴出来。demo上的代码具有一定的混论和可优化的余地,特别是采用栈的处理上。
package com.wind.ocean.livenews.parser;
import java.util.Stack;
public class ParamParser {
//定义了几种谓词,其实顺序上还是有些玄机的,后续的函数中便可以见到。如果考虑程序的通用性,应该将所有的谓词放入到一个数组中,将所有的操作符的优先级关系作出一个矩阵关系,通过二位数组进行,此程序只是个简单的demo,所有没有进行此种方法的处理
private enum verb {
rightSign,inc, dec, mul, div,leftSign
}
//定义二叉树中的节点数据结构
private class treeNode {
private treeNode lchild;
private treeNode rchild;
private verb v;
private String data;
public treeNode() {
lchild = null;
rchild = null;
}
public void setVerb(char aOp) {
if (aOp == '+') {
v = verb.inc;
} else if (aOp == '-') {
v = verb.dec;
} else if (aOp == '*') {
v = verb.mul;
} else if (aOp == '/') {
v = verb.div;
}
}
public void setData(String aData) {
data = aData;
}
}
//将给定的表达式经理线索二叉树
private treeNode createTree(String aExp) {
int oper = findLowestOpPos(aExp);
treeNode temptree = new treeNode();
if (oper > 0) {
temptree.setVerb(aExp.charAt(oper));
temptree.lchild = createTree(aExp.substring(0, oper));
temptree.rchild = createTree(aExp
.substring(oper + 1, aExp.length()));
} else if (aExp.startsWith("(") && aExp.endsWith(")")) {
temptree = createTree(aExp.substring(1, aExp.length() - 1));
} else if (!aExp.isEmpty()) {
temptree.setData(aExp);
}
return temptree;
}
//遍历二叉树采用中序遍历来计算表达式
public double caluExpbyTree(String aExp) throws Exception {
long startTime = System.currentTimeMillis();
treeNode tN = createTree(aExp);
if (null != tN && tN.lchild != null && tN.rchild != null) {
double result = calue(tN);
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
return result;
}
throw new Exception("表达式不正确");
}
//通过栈的方式计算先序表示序列
public double caluExpbyStack(String aExp) throws Exception{
long startTime = System.currentTimeMillis();
double result = 0.0;
Stack<Character> optr = new Stack<Character>();
Stack<String> opnd = new Stack<String>();
int slen = aExp.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < slen; i++) {
char tempchar = aExp.charAt(i);
if(isOp(tempchar)){
if(sb.length()>0){
opnd.push(sb.toString());
sb.delete(0, sb.length());
}
if(optr.size()==0){
optr.push(new Character(tempchar));
continue;
}
int opprior = getOpPrior(tempchar,optr);
if(opprior>0){
optr.push(new Character(tempchar));
}else if(opprior==0){
optr.pop();
}else {
double rightOpand = Double.valueOf(opnd.pop());
double leftOpand = Double.valueOf(opnd.pop());
verb op = getOpType(optr.pop()) ;
i--;//将操作数进行回退
opnd.push(String.valueOf(operate(leftOpand,op,rightOpand)));
}
}else {
sb.append(aExp.charAt(i));
}
}
if(sb.length()>0){
opnd.add(sb.toString());
}
while(optr.size()>0){
double rightOpand = Double.valueOf(opnd.pop());
double leftOpand = Double.valueOf(opnd.pop());
verb op = getOpType(optr.pop()) ;
opnd.push(String.valueOf(operate(leftOpand,op,rightOpand)));
}
result = Double.valueOf(opnd.pop())*1.0;
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
return result;
}
private boolean isOp(char aChar) {
if("+-*/()".indexOf(aChar)!=-1)
return true;
return false;
}
public int getOpPrior(char aCurOper,Stack<Character> aOptr){
Character curOp = new Character(aCurOper);
Character topOp = aOptr.peek();
if (getOpType(curOp) == verb.leftSign) {
return 1;
}else if(getOpType(curOp) == verb.rightSign){
if(getOpType(topOp) == verb.leftSign){
return 0;
}
return -1;
}else{
if(getOpType(topOp)==verb.leftSign){
return 1;
}else {
int tempresult = getOpType(curOp).compareTo(getOpType(topOp));
if(tempresult==0) tempresult =1;
return tempresult;
}
}
}
private double operate(double aLeftOperand, verb op,double aRightOperand){
double result = 0.0;
switch (op) {
case inc:
result = aLeftOperand + aRightOperand;
break;
case dec:
result = aLeftOperand - aRightOperand;
break;
case mul:
result = aLeftOperand * aRightOperand;
break;
case div:
result = aLeftOperand / aRightOperand;
break;
}
return result;
}
public verb getOpType(char aOp){
verb result = verb.inc;
switch (aOp) {
case '+':
result = verb.inc;
break;
case '-':
result = verb.dec;
break;
case '*':
result = verb.mul;
break;
case '/':
result = verb.div;
break;
case '(':
result = verb.leftSign;
break;
case ')':
result = verb.rightSign;
break;
}
return result;
}
private double calue(treeNode aTreeNode) {
double result = 0.0;
if (aTreeNode.lchild != null && aTreeNode.rchild != null) {
switch (aTreeNode.v) {
case inc:
result = calue(aTreeNode.lchild) + calue(aTreeNode.rchild);
break;
case dec:
result = calue(aTreeNode.lchild) - calue(aTreeNode.rchild);
break;
case mul:
result = calue(aTreeNode.lchild) * calue(aTreeNode.rchild);
break;
case div:
result = calue(aTreeNode.lchild) / calue(aTreeNode.rchild);
break;
}
} else {
result = Integer.valueOf(aTreeNode.data) * 1.00;
}
return result;
}
//此方法中的case顺序同样有讲究的,也就是+—和*/的case顺序,这个关于优先级的处理其实采用了一些小技巧
private int findLowestOpPos(String aExp) {
int result = 0;
int matchCount = 0;
int slen = aExp.length();
for (int i = 0; i < slen; i++) {
switch (aExp.charAt(i)) {
case '(':
matchCount++;
break;
case ')':
matchCount--;
break;
case '+':
case '-':
if (matchCount == 0) {
result = i;//到这后大家可能会想到既然此符号的优先级最低且所有的括号
//均匹配了,为什么还要继续循环呢,其实不然这里的所有操作符均认为是二元操作符如果第一结束就会有问题,如表达式5+3-2
//此时如果遇到第一个-号就结束那么—号就成为了根,而第一个5成为左孩子,而5+3成了右孩子,这样不就违反了表达式的本身顺序了嘛
}
break;
case '*':
case '/':
if (result==0 && matchCount == 0) {
result = i;
}
break;
}
}
return result;
}
public static void main(String args[]) {
try {
ParamParser pp = new ParamParser();
//System.out.println("请输入表达式--此计算采用了二叉树的中序遍历");
System.out.println(pp.caluExpbyStack("12*(5-5+3)*(5+3*5)/8+20-10+10+20*12*(3+5)*(5+3*5)/8+20*12*(3+5)*(5+3*5)/8+20"));
/*BufferedReader bReader = new BufferedReader(new InputStreamReader(
System.in));*/
System.out.println(pp.caluExpbyTree("12*(5-5+3)*(5+3*5)/8+20-10+10+20*12*(3+5)*(5+3*5)/8+20*12*(3+5)*(5+3*5)/8+20"));
// System.out.println(pp.caluExp("2*(3+5)*(5+3*(5)/8+20"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}