激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 使用java處理字符串公式運算的方法

使用java處理字符串公式運算的方法

2019-12-23 16:49JAVA教程網 JAVA教程

本篇文章介紹了,使用java處理字符串公式運算的方法。需要的朋友參考下

在改進一個關于合同的項目時,有個需求,就是由于合同中非數據項的計算公式會根據年份而進行變更,而之前是將公式硬編碼到系統中的,只要時間一變,系統就沒法使用了,因此要求合同中各個非基礎數據的項都能自定義公式,根據設置的公式來自動生成報表和合同中的數據。

  顯然定義的公式都是以字符串來存儲到數據庫的,可是java中沒有這種執行字符串公式的工具或者類,而且是公式可以嵌套一個中間公式。比如:基礎數據dddd是56,而一個公式是依賴dddd的,eeee=dddd*20,而最終的公式可能是這樣:eeee*-12+13-dddd+24。可知eeee是一個中間公式,所以一個公式的計算需要知道中間公式和基礎數據。

這好像可以使用一個解釋器模式來解決,但是我沒有成功,因為括號的優先級是一個棘手的問題,后來又想到可以使用freemarker類似的模板引擎或者java6之后提供的ScriptEngine 腳本引擎,做了個實驗,腳本引擎可以解決,但是這限制了必須使用java6及以上的版本。最終功夫不負有心人,終于找到了完美解決方案,即后綴表達式。我們平時寫的公式稱作中綴表達式,計算機處理起來比較困難,所以需要先將中綴表達式轉換成計算機處理起來比較容易的后綴表達式。

將中綴表達式轉換為后綴表達式具體算法規則:見后綴表達式


   a.若為 '(',入棧;

   b.若為 ')',則依次把棧中的的運算符加入后綴表達式中,直到出現'(',從棧中刪除'(' ;

   c.若為 除括號外的其他運算符 ,當其優先級高于棧頂運算符時,直接入棧。否則從棧頂開始,依次彈出比當前處理的運算符優先級高和優先級相等的運算符,直到一個比它優先級低的或者遇到了一個左括號為止。

   ·當掃描的中綴表達式結束時,棧中的的所有運算符出棧; 

我們提出的要求設想是這樣的:

復制代碼代碼如下:

public class FormulaTest {
     @Test
     public void testFormula() {
         //基礎數據
         Map<String, BigDecimal> values = new HashMap<String, BigDecimal>();
         values.put("dddd", BigDecimal.valueOf(56d));

         //需要依賴的其他公式
         Map<String, String> formulas = new HashMap<String, String>();
         formulas.put("eeee", "#{dddd}*20");

         //需要計算的公式
         String expression = "#{eeee}*-12+13-#{dddd}+24";

         BigDecimal result = FormulaParser.parse(expression, formulas, values);
         Assert.assertEquals(result, BigDecimal.valueOf(-13459.0));
     }
 }


以下就是解決問題的步驟:

 

1、首先將所有中間變量都替換成基礎數據

FormulaParser的finalExpression方法會將所有的中間變量都替換成基礎數據,就是一個遞歸的做法

復制代碼代碼如下:

public class FormulaParser {
     /**
      * 匹配變量占位符的正則表達式
      */
     private static Pattern pattern = Pattern.compile("\\#\\{(.+?)\\}");

     /**
      * 解析公式,并執行公式計算
      * 
      * @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);
     }

     /**
      * 解析公式,并執行公式計算
      * 
      * @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);
     }

     /**
      * 解析公式,并執行公式計算
      * 
      * @param formula
      * @return
      */
     public static BigDecimal parse(String formula) {
         return parse(formula, Collections.<String, String> emptyMap(), Collections.<String, BigDecimal> emptyMap());
     }

     /**
      * 將所有中間變量都替換成基礎數據
      * 
      * @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、將中綴表達式轉換為后綴表達式

 

  Calculator的infix2Suffix將中綴表達式轉換成了后綴表達式

3、計算后綴表達式

  Calculator的evalInfix計算后綴表達式

復制代碼代碼如下:

public class Calculator{
     private static Log logger = LogFactory.getLog(Calculator.class);

     /**
      * 左括號
      */
     public final static char LEFT_BRACKET = '(';

     /**
      * 右括號
      */
     public final static char RIGHT_BRACKET = ')';

     /**
      * 中綴表達式中的空格,需要要忽略
      */
     public final static char BLANK = ' ';

     /**
      * 小數點符號
      */
     public final static char DECIMAL_POINT = '.';

     /**
      * 負號
      */
     public final static char NEGATIVE_SIGN = '-';

     /**
      * 正號
      */
     public final static char POSITIVE_SIGN = '+';

     /**
      * 后綴表達式的各段的分隔符
      */
     public final static char SEPARATOR = ' ';

     /**
      * 解析并計算表達式
      * 
      * @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);
     }

     /**
      * 對后綴表達式進行計算
      * 
      * @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());
     }

     /**
      * 將中綴表達式轉換為后綴表達式<br>
      * 具體算法規則 81      * 1)計算機實現轉換: 將中綴表達式轉換為后綴表達式的算法思想: 
      *     開始掃描; 
      *         數字時,加入后綴表達式; 
      *         運算符: 
      *  a.若為 '(',入棧;
      *  b.若為 ')',則依次把棧中的的運算符加入后綴表達式中,直到出現'(',從棧中刪除'(' ; 
      *  c.若為 除括號外的其他運算符 ,當其優先級高于棧頂運算符時,直接入棧。否則從棧頂開始,依次彈出比當前處理的運算符優先級高和優先級相等的運算符,直到一個比它優先級低的或者遇到了一個左括號為止。
      *  ·當掃描的中綴表達式結束時,棧中的的所有運算符出棧; 
      * 
      * @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];

             // 空白則跳過
             if (c == BLANK)continue;

             // Next line is used output stack information.
             // System.out.printf("%-20s %s%n", stack, sb.toString());

             // 添加后綴表達式分隔符
             if (appendSeparator) {
                 sb.append(SEPARATOR);
                 appendSeparator = false;
             }

             if (isSign(c) && sign) {
                 sb.append(c);
             } else if (isNumber(c)) {
                 sign = false;// 數字后面不是正號或負號,而是操作符+-
                 sb.append(c);
             } else if (isLeftBracket(c)) {
                 stack.push(c);
             } else if (isRightBracket(c)) {
                 sign = false;

                 // 如果為),則彈出(上面的所有操作符,并添加到后綴表達式中,并彈出(
                 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();
     }

     /**
      * 判斷某個字符是否是正號或者負號
      * 
      * @param c
      * @return
      */
     private boolean isSign(char c) {
         return (c == NEGATIVE_SIGN || c == POSITIVE_SIGN);
     }

     /**
      * 判斷某個字符是否為數字或者小數點
      * 
      * @param c
      * @return
      */
     private boolean isNumber(char c) {
         return ((c >= '0' && c <= '9') || c == DECIMAL_POINT);
     }

     /**
      * 判斷某個字符是否為左括號
      * 
      * @param c
      * @return
      */
     private boolean isLeftBracket(char c) {
         return c == LEFT_BRACKET;
     }

     /**
      * 判斷某個字符是否為右括號
      * 
      * @param c
      * @return
      */
     private boolean isRightBracket(char c) {
         return c == RIGHT_BRACKET;
     }


最后把操作符類貼上

復制代碼代碼如下:

View Code 
 public abstract class Operator {
     /**
      * 運算符
      */
     private char operator;

     /**
      * 運算符的優先級別,數字越大,優先級別越高
      */
     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);
     }

     /**
      * 加法運算
      */
     public final static Operator ADITION = new Operator('+', 100) {
         public BigDecimal eval(BigDecimal left, BigDecimal right) {
             return left.add(right);
         }
     };

     /**
      * 減法運算
      */
     public final static Operator SUBTRATION = new Operator('-', 100) {
         public BigDecimal eval(BigDecimal left, BigDecimal right) {
             return left.subtract(right);
         }
     };

     /**
      * 乘法運算
      */
     public final static Operator MULTIPLICATION = new Operator('*', 200) {
         public BigDecimal eval(BigDecimal left, BigDecimal right) {
             return left.multiply(right);
         }
     };

     /**
      * 除法運算
      */
     public final static Operator DIVITION = new Operator('/', 200) {
         public BigDecimal eval(BigDecimal left, BigDecimal right) {
             return left.divide(right);
         }
     };

     /**
      * 冪運算
      */
     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;
     }

     /**
      * 根據某個運算符獲得該運算符的優先級別
      * 
      * @param c
      * @return 運算符的優先級別
      */
     public static int getPrority(char c) {
         Operator op = operators.get(c);
         return op != null ? op.getPriority() : 0;
     }

     /**
      * 工具方法,判斷某個字符是否是運算符
      * 
      * @param c
      * @return 是運算符返回 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));
     }

     /**
      * 根據運算符獲得 Operator 實例
      * 
      * @param c
      * @return 從注冊中的 Operator 返回實例,尚未注冊返回 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));
     }

     /**
      * 根據操作數進行計算
      * 
      * @param left
      *            左操作數
      * @param right
      *            右操作數
      * @return 計算結果
      */
     public abstract BigDecimal eval(BigDecimal left, BigDecimal right);

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 被摁着灌浓精囚禁高h1v1 | 91精品国产福利尤物免费 | 49vv看片免费 | 亚洲精品无码不卡在线播放he | 欧美成人精品欧美一级乱黄 | 视频在线亚洲 | 国产色91| 91 在线| 成人在线视频播放 | 国产乱淫a∨片免费视频 | 爱逼爱操综合网 | 国产一区二区精彩视频 | 青青草免费观看完整版高清 | 在线免费观看精品 | 久久久久久久久日本理论电影 | 吾色视频 | 成人aaaa免费全部观看 | 72pao成人国产永久免费视频 | 中文字幕网站在线 | 啪啪激情 | 久草成人在线观看 | 国产一级一级片 | 伦一区二区三区中文字幕v亚洲 | 最新中文字幕在线视频 | 国产精品久久久久久久久久三级 | 色播久久 | 13一14毛片免费看 | 在线日韩 | 午夜视频在线观看免费视频 | 亚洲第一成人在线观看 | 黄色毛片免费看 | 国产精品99久久免费观看 | 99热草| 黄视频网站免费在线观看 | 日本中文字幕久久 | 欧美成人一级 | 国产精品久久久久久影视 | 黄色免费视频网站 | 欧美一级美国一级 | 精品久久久久久久久久久久久久 | 久久无毛 |