国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看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??芍猠eee是一個中間公式,所以一個公式的計算需要知道中間公式和基礎數據。

這好像可以使用一個解釋器模式來解決,但是我沒有成功,因為括號的優先級是一個棘手的問題,后來又想到可以使用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);

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 我要看免费黄色片 | 中文字幕乱码亚洲精品一区 | 欧美人妖在线 | 精品在线一区二区 | 国产成人在线一区二区 | 一级黄色片子看看 | 转生成为史莱姆这档事第四季在线观看 | 一区视频在线 | 日韩欧美视频一区 | 激情视频在线观看 | 午夜一区二区三区 | 国产一区二区三区四 | 久久精品无码一区二区日韩av | 欧美日韩国产影院 | 久久久www | 激情欧美日韩一区二区 | 久久精品国产99国产精2020新增功能 | 国产在线国产 | 亚洲xxxx3d | 欧美一区二区在线观看 | 狠狠干最新网址 | 午夜视频在线观看网站 | 国产精品一区二区不卡 | 97精品国产97久久久久久免费 | 在线观看国产成人av片 | 国产精品第一区 | 日韩三级视频 | 黄色毛片在线视频 | 亚洲一区免费视频 | 久久久精品呻吟 | 欧美激情网址 | 久久久国产一区二区三区 | 一级黄色片aaa | 蜜桃av一区二区三区 | 天堂资源在线 | 蜜桃成人在线 | 黑人一区 | a天堂国产| 国产999精品久久久久久麻豆 | 免费国产一区 | 黄色影片免费在线观看 |