支持加减乘除和四则运算
测试类:
package com.enter.swing;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.TreeMap;
public class Test12 {
public static void main(String[] args) {
Test12 test=new Test12();
ArrayList<String> formulals=new ArrayList<>();//计算公式集合
formulals.add("金额=数量*单价*(1+1)");
ArrayList Caldatas=new ArrayList<>();//计算数据集合
ArrayList als=new ArrayList<>();//第一组计算集合
als.add(10);//数量
als.add(5);//单价
als.add(0);//金额
Caldatas.add(als);//添加到计算数据集合
ArrayList als1=new ArrayList<>();//第二组计算集合
als1.add(5);//数量
als1.add(5);//单价
als1.add(0);//金额
Caldatas.add(als1);//添加到计算数据集合
ArrayList<String> colnumCals=new ArrayList<>();//计算数据的列名
colnumCals.add("数量");
colnumCals.add("单价");
colnumCals.add("金额");
ArrayList calformula = test.Calformula(formulals, Caldatas, colnumCals);
System.out.println(calformula);
}
/**
* 自定义公式计算
* @param formulals 计算公式集合
* @param Caldatas 计算数据集合
* @param colnumCals 列名集合
* @return
*/
private ArrayList Calformula(ArrayList<String> formulals,ArrayList Caldatas,ArrayList<String> colnumCals){
if(formulals==null||formulals.size()==0){
return Caldatas;
}
ArrayList als=new ArrayList<>();
char[] cArray ={'+','-','*','/','(',')','[',']','{','}','='};//定义运算符
Map<String,String> formulamap=new HashMap<>();
Map<String, String> sortMap = new TreeMap<>();
for (int i1 = 0; i1 < formulals.size(); i1++) {
String fkey="";//键:结果
String fvalue="";//值:公式
String[] formulasplit = formulals.get(i1).split("=");
if(formulasplit.length==2){
for (int j1 = 0; j1 < formulasplit.length; j1++) {
boolean validationformula = Validationformula(formulasplit[j1]);//检查是否有运算符
if(validationformula){
fvalue=formulasplit[j1];
}else{
fkey=formulasplit[j1];
sortMap.put(i1+"", fkey);
}
}
}else{
continue;
}
formulamap.put(fkey, fvalue);
}
Map<String,Integer> formulaCellmap=new HashMap<>();//用来储存列名称和列下标
for (int i1 = 0; i1 < formulals.size(); i1++) {
String str=formulals.get(i1);
String rs="";
char[] charArray = str.toCharArray();
for (int j1 = 0; j1 < charArray.length; j1++) {
for (int k = 0; k < cArray.length; k++) {
if(charArray[j1]==cArray[k]){
charArray[j1]=',';
}
}
if(!(charArray[j1]=='.')){
if (charArray[j1] < '0' || charArray[j1] > '9') {
rs+=charArray[j1];
}
}
}
String[] split = rs.split(",");
for (int j = 0; j < split.length; j++) {
for (int i = 0; i < colnumCals.size(); i++) {
String col = colnumCals.get(i);
if(col!=null&&!col.equals("")&&col.trim().equals(split[j].trim())){
formulaCellmap.put(col, i);
}
}
}
}
Map<Integer,String> mapCal=null;
Map<String,String> fmap=null;
for (int j = 0; j < Caldatas.size(); j++) {
ArrayList Caldata=(ArrayList)Caldatas.get(j);
mapCal=new HashMap<>();
fmap=new HashMap<>();
for (int i = 0; i < Caldata.size(); i++) {
for (Entry<String, Integer> entry : formulaCellmap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
if(value==i){
fmap.put(key,Caldata.get(i)==null?"":Caldata.get(i).toString().trim());
}
}
if(i==Caldata.size()-1){
for (Entry<String, String> entry : sortMap.entrySet()) {//这里遍历的是排序map,所以mapKey是value,mapValue是根据mapKey从formulamap中拿的
String mapKey = entry.getValue();//计算结果名称
String mapValue = formulamap.get(entry.getValue());//计算公式
String str=mapValue;
String rs="";
char[] charArray = str.toCharArray();
for (int j1 = 0; j1 < charArray.length; j1++) {
for (int k = 0; k < cArray.length; k++) {
if(charArray[j1]==cArray[k]){
charArray[j1]=',';
}
}
if(!(charArray[j1]=='.')){
if (charArray[j1] < '0' || charArray[j1] > '9') {
rs+=charArray[j1];
}
}
}
String[] split = rs.split(",");
for (int k = 0; k < split.length; k++) {
for (Map.Entry<String, String> entry1 : fmap.entrySet()) {
String mapKey1 = entry1.getKey();
String mapValue1 = entry1.getValue();
if(split[k]!=null&&!split[k].equals("")&&split[k].trim().equals(mapKey1.trim())){
str = str.replace(split[k], mapValue1);
}
}
}
boolean b1=false;
char[] charArr=str.toCharArray();
for (int k = 0; k < charArr.length; k++) {
boolean b=false;
for (int k2 = 0; k2 < cArray.length; k2++) {
if(charArr[k]==cArray[k2]){
b=true;
break;
}else {
b=false;
}
}
if(b){
b1=true;
}else{
if(charArr[k]=='.'){
b1=true;
}else if(!(charArr[k]<'0')&&!(charArr[k]>'9')){
b1=true;
}else{
b1=false;
break;
}
}
}
if(b1){
String calValue=cal(charArr)[0];//计算结果
if(formulaCellmap.get(mapKey)!=null&&!calValue.equals("N")){
mapCal.put(formulaCellmap.get(mapKey), calValue);
fmap.put(mapKey, calValue);//将计算结果放入存放计算列和对应值的map,可能下个算式会用到
}
}
}
}
}
List tmpList=new ArrayList<>();
for (int k = 0; k < Caldata.size(); k++) {
String tvalue="";
boolean b=false;
for (Entry<Integer, String> entry : mapCal.entrySet()) {
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if(k==mapKey){
b=true;
tvalue=mapValue;
}
}
if(b){
tmpList.add(tvalue);
}else{
tmpList.add(Caldata.get(k));
}
}
als.add(tmpList) ;
}
return als;
}
/**
* 计算的主要实现方法
*/
public static String[] cal(char[] charArr) {
String sum = "0"; // 和
int len = charArr.length;
Stack<String> stack = new Stack<>(); // 栈
int i = 0; // 因为要利用i来计算以此递归处理了多少个字符,所以放到外面来定义
try {
for (; i < charArr.length; i++) {
// result记录两个数据,result[0]是本次递归处理的数的和。result[1]是本次递归处理了多少个字符
String[] result;
if (charArr[i] == ')' || charArr[i] == ']' || charArr[i] == '}') { // 遇到右括号说明本次递归结束
break;
} else if (charArr[i] == '(' || charArr[i] == '[' || charArr[i] == '{') {//遇到左括号就进入新的递归
// 递归,入参是当前字符+1到末尾。因为并不知道右括号在什么位置,所以直接取到末尾
result = cal(Arrays.copyOfRange(charArr, i + 1, len));
stack.push(result[0]);// 讲一组括号内计算的值入栈
i += Integer.parseInt(result[1]); // 递归是处理了一批字符的,所以下次处理是当前位置加上递归处理的字符长度,继续处理
} else if (charArr[i] >= '0' && charArr[i] <= '9') { //遇到数字的处理
// 因为字符数组存储的是单个数字,而真正的数字肯定有好几位数,所以需要讲连续的几个数字字符合并成一个整数
result = findNum(i, charArr);
stack.push(result[0]); //取到数字先入栈
i += Integer.parseInt(result[1]) - 1;
} else {
// 获取运算符后面的数
result = findNum(i + 1, charArr);
int tempI=i; // 后面的i可能会变动,所以要记录下当前值
if (Integer.parseInt(result[1]) == 0) { // 若寻找数字时一个字符都没运算,则可以确定后面后面跟的是括号
i++; //跳过括号
result = cal(Arrays.copyOfRange(charArr, i + 1, len));//是括号则递归求值
}
if (charArr[tempI] == '*') {
String number1 = stack.pop(); // 遇到乘除要先出栈,与后面的数字计算完再次入栈,因为栈中只存储加法运算
String number2 = result[0];
stack.push(String.valueOf(mul(number1, number2)));// 计算完在入栈
} else if (charArr[tempI] == '/') {
String number1 = stack.pop();
String number2 = result[0];
stack.push(String.valueOf(div(number1, number2)));
} else if (charArr[tempI] == '-') {
stack.push("-"+result[0]); // 减法相当于加负数
} else if (charArr[tempI] == '+') {
stack.push(result[0]);
}
i += Integer.parseInt(result[1]); // 确定当前运算到什么位置了
}
}
//将栈中数字求和
while (!stack.empty()) {
//sum += Integer.parseInt(stack.pop());
sum=String.valueOf(add(sum, stack.pop()));
}
} catch (Exception e) {
// TODO: handle exception
return new String[]{"N"};
}
return new String[]{sum,i+1+""};// 第一个数是当前栈中的和,第二个数字是本次递归处理的字符数
}
/**
* 整合连续几个字符数字为一个完整的整数
*/
public static String[] findNum(int index, char[] charArr) {
int i = index;
int fuHaoWei = 1; // 可能会遇到负数,所以最终要乘以符号位,默认为1,即正整数
// 判断是否为负数
if (charArr[i] == '-') {
fuHaoWei *= -1; //若为负数则符号位=-1
i++; // 此时处理了一个“-”,也要记得加1
}
// 计算连续数字的长度,找到这个范围
for (; i < charArr.length; i++) {
// 当遇到超出数字字符范围的字符,即连续数字结束
if(!(charArr[i]=='.')){
if (charArr[i] < '0' || charArr[i] > '9') {
break;
}
}
}
if (fuHaoWei == -1) {
//输出还是上面那个result数组。负数因为多了个“-”开头,所以index + 1
return new String[]{"-"+pinShuzi(Arrays.copyOfRange(charArr, index + 1, i)),( i - index)+""};
} else {
return new String[]{(pinShuzi(Arrays.copyOfRange(charArr, index, i)))+"", (i - index)+""};
}
}
/**
*
* 将连续数字拼接成一个真正的整数,这里用位运算可能更优雅
*/
public static String pinShuzi(char[] charArr) {
String sum = "";
for (int i = 0; i <charArr.length; i++) {
sum += String.valueOf(charArr[i]);
}
return sum;
}
@SuppressWarnings("unused")
public static boolean Validationformula(String param){
boolean b=true;
if(param.indexOf("+")!=-1){
b=false;
}else if(param.indexOf("-")!=-1){
b=false;
}else if(param.indexOf("*")!=-1){
b=false;
}else if(param.indexOf("/")!=-1){
b=false;
}else if(param.indexOf("(")!=-1){
b=false;
}else if(param.indexOf(")")!=-1){
b=false;
}else if(param.indexOf("[")!=-1){
b=false;
}else if(param.indexOf("]")!=-1){
b=false;
}else if(param.indexOf("{")!=-1){
b=false;
}else if(param.indexOf("}")!=-1){
b=false;
}
return !b;
}
/**
* 公式计算
* @param Formulals公式
* @param params参数
* @return
*/
private String Formularesults(String formula){
return null;
}
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 16;
/**
* 加法
* @param v1
* @param v2
* @return
*/
public static double add(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.add(b2).doubleValue();
}
/**
* 减法
* @param v1
* @param v2
* @return
*/
public static double sub(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.subtract(b2).doubleValue();
}
/**
* 乘法
* @param v1
* @param v2
* @return
*/
public static double mul(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.multiply(b2).doubleValue();
}
/**
* 除法
* @param v1
* @param v2
* @return
*/
public static double div(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
输出结果: