使用java處理字符串公式運(yùn)算的方法_MySQL教程
推薦:用MyEclipse配置DataBase Explorer(圖示)本文介紹了,用MyEclipse配置DataBase Explorer的圖片示例。需要的朋友參考下
在改進(jìn)一個(gè)關(guān)于合同的項(xiàng)目時(shí),有個(gè)需求,就是由于合同中非數(shù)據(jù)項(xiàng)的計(jì)算公式會(huì)根據(jù)年份而進(jìn)行變更,而之前是將公式硬編碼到系統(tǒng)中的,只要時(shí)間一變,系統(tǒng)就沒(méi)法使用了,因此要求合同中各個(gè)非基礎(chǔ)數(shù)據(jù)的項(xiàng)都能自定義公式,根據(jù)設(shè)置的公式來(lái)自動(dòng)生成報(bào)表和合同中的數(shù)據(jù)。
顯然定義的公式都是以字符串來(lái)存儲(chǔ)到數(shù)據(jù)庫(kù)的,可是java中沒(méi)有這種執(zhí)行字符串公式的工具或者類(lèi),而且是公式可以嵌套一個(gè)中間公式。比如:基礎(chǔ)數(shù)據(jù)dddd是56,而一個(gè)公式是依賴(lài)dddd的,eeee=dddd*20,而最終的公式可能是這樣:eeee*-12+13-dddd+24�?芍猠eee是一個(gè)中間公式,所以一個(gè)公式的計(jì)算需要知道中間公式和基礎(chǔ)數(shù)據(jù)。
這好像可以使用一個(gè)解釋器模式來(lái)解決,但是我沒(méi)有成功,因?yàn)槔ㄌ?hào)的優(yōu)先級(jí)是一個(gè)棘手的問(wèn)題,后來(lái)又想到可以使用freemarker類(lèi)似的模板引擎或者java6之后提供的ScriptEngine 腳本引擎,做了個(gè)實(shí)驗(yàn),腳本引擎可以解決,但是這限制了必須使用java6及以上的版本。最終功夫不負(fù)有心人,終于找到了完美解決方案,即后綴表達(dá)式。我們平時(shí)寫(xiě)的公式稱(chēng)作中綴表達(dá)式,計(jì)算機(jī)處理起來(lái)比較困難,所以需要先將中綴表達(dá)式轉(zhuǎn)換成計(jì)算機(jī)處理起來(lái)比較容易的后綴表達(dá)式。
將中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式具體算法規(guī)則:見(jiàn)后綴表達(dá)式
a.若為 '(',入棧;
b.若為 ')',則依次把棧中的的運(yùn)算符加入后綴表達(dá)式中,直到出現(xiàn)'(',從棧中刪除'(' ;
c.若為 除括號(hào)外的其他運(yùn)算符 ,當(dāng)其優(yōu)先級(jí)高于棧頂運(yùn)算符時(shí),直接入棧。否則從棧頂開(kāi)始,依次彈出比當(dāng)前處理的運(yùn)算符優(yōu)先級(jí)高和優(yōu)先級(jí)相等的運(yùn)算符,直到一個(gè)比它優(yōu)先級(jí)低的或者遇到了一個(gè)左括號(hào)為止。
·當(dāng)掃描的中綴表達(dá)式結(jié)束時(shí),棧中的的所有運(yùn)算符出棧;
我們提出的要求設(shè)想是這樣的:
public class FormulaTest {
@Test
public void testFormula() {
//基礎(chǔ)數(shù)據(jù)
Map<String, BigDecimal> values = new HashMap<String, BigDecimal>();
values.put("dddd", BigDecimal.valueOf(56d));
//需要依賴(lài)的其他公式
Map<String, String> formulas = new HashMap<String, String>();
formulas.put("eeee", "#{dddd}*20");
//需要計(jì)算的公式
String expression = "#{eeee}*-12+13-#{dddd}+24";
BigDecimal result = FormulaParser.parse(expression, formulas, values);
Assert.assertEquals(result, BigDecimal.valueOf(-13459.0));
}
}
以下就是解決問(wèn)題的步驟:
1、首先將所有中間變量都替換成基礎(chǔ)數(shù)據(jù)
FormulaParser的finalExpression方法會(huì)將所有的中間變量都替換成基礎(chǔ)數(shù)據(jù),就是一個(gè)遞歸的做法
public class FormulaParser {
/**
* 匹配變量占位符的正則表達(dá)式
*/
private static Pattern pattern = Pattern.compile("\\#\\{(.+?)\\}");
/**
* 解析公式,并執(zhí)行公式計(jì)算
*
* @param formula
* @param formulas
* @param values
* @return
*/
public static BigDecimal parse(String formula, Map<String, String> formulas, Map<String, BigDecimal> values) {
if (formulas == null)formulas = Collections.emptyMap();
if (values == null)values = Collections.emptyMap();
String expression = finalExpression(formula, formulas, values);
return new Calculator().eval(expression);
}
/**
* 解析公式,并執(zhí)行公式計(jì)算
*
* @param formula
* @param values
* @return
*/
public static BigDecimal parse(String formula, Map<String, BigDecimal> values) {
if (values == null)values = Collections.emptyMap();
return parse(formula, Collections.<String, String> emptyMap(), values);
}
/**
* 解析公式,并執(zhí)行公式計(jì)算
*
* @param formula
* @return
*/
public static BigDecimal parse(String formula) {
return parse(formula, Collections.<String, String> emptyMap(), Collections.<String, BigDecimal> emptyMap());
}
/**
* 將所有中間變量都替換成基礎(chǔ)數(shù)據(jù)
*
* @param expression
* @param formulas
* @param values
* @return
*/
private static String finalExpression(String expression, Map<String, String> formulas, Map<String, BigDecimal> values) {
Matcher m = pattern.matcher(expression);
if (!m.find())return expression;
m.reset();
StringBuffer buffer = new StringBuffer();
while (m.find()) {
String group = m.group(1);
if (formulas != null && formulas.containsKey(group)) {
String formula = formulas.get(group);
m.appendReplacement(buffer, '(' + formula + ')');
} else if (values != null && values.containsKey(group)) {
BigDecimal value = values.get(group);
m.appendReplacement(buffer,value.toPlainString());
}else{
throw new IllegalArgumentException("expression '"+expression+"' has a illegal variable:"+m.group()+",cause veriable '"+group+"' not being found in formulas or in values.");
}
}
m.appendTail(buffer);
return finalExpression(buffer.toString(), formulas, values);
}
}
2、將中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式
Calculator的infix2Suffix將中綴表達(dá)式轉(zhuǎn)換成了后綴表達(dá)式
3、計(jì)算后綴表達(dá)式
Calculator的evalInfix計(jì)算后綴表達(dá)式
public class Calculator{
private static Log logger = LogFactory.getLog(Calculator.class);
/**
* 左括號(hào)
*/
public final static char LEFT_BRACKET = '(';
/**
* 右括號(hào)
*/
public final static char RIGHT_BRACKET = ')';
/**
* 中綴表達(dá)式中的空格,需要要忽略
*/
public final static char BLANK = ' ';
/**
* 小數(shù)點(diǎn)符號(hào)
*/
public final static char DECIMAL_POINT = '.';
/**
* 負(fù)號(hào)
*/
public final static char NEGATIVE_SIGN = '-';
/**
* 正號(hào)
*/
public final static char POSITIVE_SIGN = '+';
/**
* 后綴表達(dá)式的各段的分隔符
*/
public final static char SEPARATOR = ' ';
/**
* 解析并計(jì)算表達(dá)式
*
* @param expression
* @return
*/
public BigDecimal eval(String expression) {
String str = infix2Suffix(expression);
logger.info("Infix Expression: " + expression);
logger.info("Suffix Expression: " + str);
if (str == null) {
throw new IllegalArgumentException("Infix Expression is null!");
}
return evalInfix(str);
}
/**
* 對(duì)后綴表達(dá)式進(jìn)行計(jì)算
*
* @param expression
* @return
*/
private BigDecimal evalInfix(String expression) {
String[] strs = expression.split("\\s+");
Stack<String> stack = new Stack<String>();
for (int i = 0; i < strs.length; i++) {
if (!Operator.isOperator(strs[i])) {
stack.push(strs[i]);
} else {
Operator op = Operator.getInstance(strs[i]);
BigDecimal right =new BigDecimal(stack.pop());
BigDecimal left =new BigDecimal(stack.pop());
BigDecimal result = op.eval(left, right);
stack.push(String.valueOf(result));
}
}
return new BigDecimal(stack.pop());
}
/**
* 將中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式<br>
* 具體算法規(guī)則 81 * 1)計(jì)算機(jī)實(shí)現(xiàn)轉(zhuǎn)換: 將中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式的算法思想:
* 開(kāi)始掃描;
* 數(shù)字時(shí),加入后綴表達(dá)式;
* 運(yùn)算符:
* a.若為 '(',入棧;
* b.若為 ')',則依次把棧中的的運(yùn)算符加入后綴表達(dá)式中,直到出現(xiàn)'(',從棧中刪除'(' ;
* c.若為 除括號(hào)外的其他運(yùn)算符 ,當(dāng)其優(yōu)先級(jí)高于棧頂運(yùn)算符時(shí),直接入棧。否則從棧頂開(kāi)始,依次彈出比當(dāng)前處理的運(yùn)算符優(yōu)先級(jí)高和優(yōu)先級(jí)相等的運(yùn)算符,直到一個(gè)比它優(yōu)先級(jí)低的或者遇到了一個(gè)左括號(hào)為止。
* ·當(dāng)掃描的中綴表達(dá)式結(jié)束時(shí),棧中的的所有運(yùn)算符出棧;
*
* @param expression
* @return
*/
public String infix2Suffix(String expression) {
if (expression == null) return null;
Stack<Character> stack = new Stack<Character>();
char[] chs = expression.toCharArray();
StringBuilder sb = new StringBuilder(chs.length);
boolean appendSeparator = false;
boolean sign = true;
for (int i = 0; i < chs.length; i++) {
char c = chs[i];
// 空白則跳過(guò)
if (c == BLANK)continue;
// Next line is used output stack information.
// System.out.printf("%-20s %s%n", stack, sb.toString());
// 添加后綴表達(dá)式分隔符
if (appendSeparator) {
sb.append(SEPARATOR);
appendSeparator = false;
}
if (isSign(c) && sign) {
sb.append(c);
} else if (isNumber(c)) {
sign = false;// 數(shù)字后面不是正號(hào)或負(fù)號(hào),而是操作符+-
sb.append(c);
} else if (isLeftBracket(c)) {
stack.push(c);
} else if (isRightBracket(c)) {
sign = false;
// 如果為),則彈出(上面的所有操作符,并添加到后綴表達(dá)式中,并彈出(
while (stack.peek() != LEFT_BRACKET) {
sb.append(SEPARATOR).append(stack.pop());
}
stack.pop();
} else {
appendSeparator = true;
if (Operator.isOperator(c)) {
sign = true;
// 若為(則入棧
if (stack.isEmpty() || stack.peek() == LEFT_BRACKET) {
stack.push(c);
continue;
}
int precedence = Operator.getPrority(c);
while (!stack.isEmpty() && Operator.getPrority(stack.peek()) >= precedence) {
sb.append(SEPARATOR).append(stack.pop());
}
stack.push(c);
}
}
}
while (!stack.isEmpty()) {
sb.append(SEPARATOR).append(stack.pop());
}
return sb.toString();
}
/**
* 判斷某個(gè)字符是否是正號(hào)或者負(fù)號(hào)
*
* @param c
* @return
*/
private boolean isSign(char c) {
return (c == NEGATIVE_SIGN || c == POSITIVE_SIGN);
}
/**
* 判斷某個(gè)字符是否為數(shù)字或者小數(shù)點(diǎn)
*
* @param c
* @return
*/
private boolean isNumber(char c) {
return ((c >= '0' && c <= '9') || c == DECIMAL_POINT);
}
/**
* 判斷某個(gè)字符是否為左括號(hào)
*
* @param c
* @return
*/
private boolean isLeftBracket(char c) {
return c == LEFT_BRACKET;
}
/**
* 判斷某個(gè)字符是否為右括號(hào)
*
* @param c
* @return
*/
private boolean isRightBracket(char c) {
return c == RIGHT_BRACKET;
}
最后把操作符類(lèi)貼上
View Code
public abstract class Operator {
/**
* 運(yùn)算符
*/
private char operator;
/**
* 運(yùn)算符的優(yōu)先級(jí)別,數(shù)字越大,優(yōu)先級(jí)別越高
*/
private int priority;
private static Map<Character, Operator> operators = new HashMap<Character, Operator>();
private Operator(char operator, int priority) {
setOperator(operator);
setPriority(priority);
register(this);
}
private void register(Operator operator) {
operators.put(operator.getOperator(), operator);
}
/**
* 加法運(yùn)算
*/
public final static Operator ADITION = new Operator('+', 100) {
public BigDecimal eval(BigDecimal left, BigDecimal right) {
return left.add(right);
}
};
/**
* 減法運(yùn)算
*/
public final static Operator SUBTRATION = new Operator('-', 100) {
public BigDecimal eval(BigDecimal left, BigDecimal right) {
return left.subtract(right);
}
};
/**
* 乘法運(yùn)算
*/
public final static Operator MULTIPLICATION = new Operator('*', 200) {
public BigDecimal eval(BigDecimal left, BigDecimal right) {
return left.multiply(right);
}
};
/**
* 除法運(yùn)算
*/
public final static Operator DIVITION = new Operator('/', 200) {
public BigDecimal eval(BigDecimal left, BigDecimal right) {
return left.divide(right);
}
};
/**
* 冪運(yùn)算
*/
public final static Operator EXPONENT = new Operator('^', 300) {
public BigDecimal eval(BigDecimal left, BigDecimal right) {
return left.pow(right.intValue());
}
};
public char getOperator() {
return operator;
}
private void setOperator(char operator) {
this.operator = operator;
}
public int getPriority() {
return priority;
}
private void setPriority(int priority) {
this.priority = priority;
}
/**
* 根據(jù)某個(gè)運(yùn)算符獲得該運(yùn)算符的優(yōu)先級(jí)別
*
* @param c
* @return 運(yùn)算符的優(yōu)先級(jí)別
*/
public static int getPrority(char c) {
Operator op = operators.get(c);
return op != null ? op.getPriority() : 0;
}
/**
* 工具方法,判斷某個(gè)字符是否是運(yùn)算符
*
* @param c
* @return 是運(yùn)算符返回 true,否則返回 false
*/
public static boolean isOperator(char c) {
return getInstance(c) != null;
}
public static boolean isOperator(String str) {
return str.length() > 1 ? false : isOperator(str.charAt(0));
}
/**
* 根據(jù)運(yùn)算符獲得 Operator 實(shí)例
*
* @param c
* @return 從注冊(cè)中的 Operator 返回實(shí)例,尚未注冊(cè)返回 null
*/
public static Operator getInstance(char c) {
return operators.get(c);
}
public static Operator getInstance(String str) {
return str.length() > 1 ? null : getInstance(str.charAt(0));
}
/**
* 根據(jù)操作數(shù)進(jìn)行計(jì)算
*
* @param left
* 左操作數(shù)
* @param right
* 右操作數(shù)
* @return 計(jì)算結(jié)果
*/
public abstract BigDecimal eval(BigDecimal left, BigDecimal right);
分享:Java連接MySql的詳細(xì)介紹本篇文章主要是對(duì)Java連接MySql的詳細(xì)介紹。需要的朋友參考下
- MSSQL清空日志刪除日志文件
- 關(guān)于數(shù)據(jù)庫(kù)中保留小數(shù)位的問(wèn)題
- 解析mysql與Oracle update的區(qū)別
- mysql 導(dǎo)入導(dǎo)出數(shù)據(jù)庫(kù)以及函數(shù)、存儲(chǔ)過(guò)程的介紹
- MySQL——修改root密碼的4種方法(以windows為例)
- 解決MYSQL出現(xiàn)Can''t create/write to file ''#sql_5c0_0.MYD''的問(wèn)題
- 深入理解SQL的四種連接-左外連接、右外連接、內(nèi)連接、全連接
- 解析:內(nèi)聯(lián),左外聯(lián),右外聯(lián),全連接,交叉連接的區(qū)別
- mysql出現(xiàn)“Incorrect key file for table”處理方法
- mysql重裝后出現(xiàn)亂碼設(shè)置為utf8可解決
- 淺析一個(gè)MYSQL語(yǔ)法(在查詢(xún)中使用count)的兼容性問(wèn)題
- 解析MySQL中INSERT INTO SELECT的使用
MySQL教程Rss訂閱編程教程搜索
MySQL教程推薦
- mysql中text與varchar與char的區(qū)別
- 更新text字段時(shí)出現(xiàn)Row size too large報(bào)錯(cuò)應(yīng)付措施
- SQL Server Management Studio 沒(méi)有出來(lái)
- mysql創(chuàng)建函數(shù)出現(xiàn)1418錯(cuò)誤的解決辦法
- 模板無(wú)憂:mysql數(shù)據(jù)庫(kù)優(yōu)化總結(jié)
- MySQL 關(guān)于表復(fù)制 insert into 語(yǔ)法的詳細(xì)介紹
- 設(shè)置mysql的sql_mode
- MySQL查詢(xún)優(yōu)化:用子查詢(xún)代替非主鍵連接查詢(xún)實(shí)例介紹
- MySQL筆記之基本查詢(xún)的應(yīng)用詳解
- 基于mysql全文索引的深入理解
猜你也喜歡看這些
- 詳解:SQL Server 2000 的各種版本
- 讓SQL Server數(shù)據(jù)庫(kù)自動(dòng)執(zhí)行管理任務(wù)(二)
- 淺析SQL子查詢(xún)實(shí)例
- SQL“多字段模糊匹配關(guān)鍵字查詢(xún)”
- 怎樣用SQL Server事件探查器創(chuàng)建跟蹤
- SQL Server連接中常見(jiàn)錯(cuò)誤的解決方法
- 分析SQL Server性能的改進(jìn)與邏輯數(shù)據(jù)庫(kù)設(shè)計(jì)的關(guān)聯(lián)
- sql server查詢(xún)時(shí)間技巧分享
- SQL server 管理事務(wù)和數(shù)據(jù)庫(kù)介紹
- 詳解SQL Server2000安全管理機(jī)制
- 相關(guān)鏈接:
- 教程說(shuō)明:
MySQL教程-使用java處理字符串公式運(yùn)算的方法
。