java字符串應用之表達式解析器

          一、表達式的組成
              1、數字
              2、運算符:+ - / * ^ % =
              3、圓括號
              4、變量
          二、運算符優先級
              由高到低分別為:+-(正負號)、^、*/%、+-、=
              優先級相等的運算符按照從左到右的順序計算
          三、關鍵技術點
              1、確定運算的優先級,從高到低分別為:原子元素表達式,包括數字和變量;括號表達式;一元表達式,取數的負數;指數表達式;乘、除、取模表達式;加、減表達式;賦值表達式。
              2、對于每一級別的運算,都由一個方法實現,在方法中先完成比自己高一級別的運算,再處理本級別的運算。因此,在計算整個表達式的主方法中,只需要調用最低級別的運算的實現方法即可。
              3、確定表達式中的分隔符,(+、-、*、/、%、^、=、(、)、)。利用這些分隔符將表達式分成多段,每一段叫做一個token,分隔符也算token。
              4、用長度為26的int數組vars存儲變量的值。
              5、Character的isWhitespace方法判斷字符是否為空白符,用于去掉表達式中的空白符。
              6、Character的isLetter方法判斷字符是否為字母,用于提取表達式中的變量
              7、Character的isDigit方法判斷字符是否為數字,用于獲取表達式中的數字
             
          四、演示實例

           

          /**
           * 文件名ExpressionParser.java
           
          */

          package book.oo.String;

          /**
           * 表達式解析器
           * 
          @author joe
           *
           
          */

          public class ExpressionParser {
              
          //4種標記類型
              public static final int NONE_TOKEN = 0;    //標記為空或者結束符
              public static final int DELIMITER_TOKEN = 1;    //標記為分隔符
              public static final int VARIABLE_TOKEN = 2;    //標記為變量
              public static final int NUMBER_TOKEN = 3;    //標記為數字
              
              
          //4種錯誤類型
              public static final int SYNTAX_ERROR = 0;    //語法錯誤
              public static final int UNBALPARENS_ERROR = 1;    //括號沒有結束錯誤
              public static final int NOEXP_ERROR = 2;    //表達式為空錯誤
              public static final int DIVBYZERO_ERROR = 3;    //被0除錯誤
              
              
          //針對4種錯誤類型定義的4個錯誤提示
              public static final String[] ERROR_MESSAGES = {"Syntax Error""Unbalanced " +
                      
          "Parentheses""No Expression Present""Division by Zero"}
          ;
              
              
          //表達式的結束標記
              public static final String EOE = ""\0";
           
           private String exp; //表達式字符串
           private int expIndex; //解析器當前指針在表達式中的位置
           private String token; //解析器當前處理的標記
           private int tokenType; //解析器當前處理的標記類型
           private double[] vars = new double[26]; //變量數組
           /**
            *
            */
           public ExpressionParser() {
           }
           
           /**
            * 解析一個表達式,返回表達式的值
            */
           public double evaluate(String expStr) throws Exception {
            double result;
            this.exp = expStr;
            this.expIndex = 0;
            
            //獲取第一個標記
            this.getToken();
            if (this.token.equals(EOE)) {
             //沒有表達式異常
             this.handleError(NOEXP_ERROR);
            }
            
            result = this.parseAssign(); //處理賦值語句
            //處理完賦值語句,應該就是表達式結束符,如果不是,則返回異常
            if(!this.token.equals(EOE)) {
             this.handleError(SYNTAX_ERROR);
            }
            return result;
           }
           
           /**
            * 處理賦值語句
            */
           public double parseAssign() throws Exception {
            double result; //結果
            int varIndex; //變量下標
            String oldToken; //舊標記
            int oldTokenType; //舊標記的類型
            
            //如果標記類型是變量
            if (this.tokenType == VARIABLE_TOKEN) {
             //保存當前標記
             oldToken = new String(this.token);
             oldTokenType = this.tokenType;
             //取得變量的索引,本解析器只支持一個字母的變量
             //如果用戶的變量字母長度大于1,則取第一個字母當作變量
             varIndex = Character.toUpperCase(this.token.charAt(0)) - 'A';
             
             //獲得下一個標記
             this.getToken();
             //如果當前標記不是等號=
             if(!this.token.equals("=")) {
              this.putBack(); //回滾
              //不是一個賦值語句,將標記恢復到上一個標記
              this.token = new String(oldToken);
              this.tokenType = oldTokenType;
             } else {
              //如果當前標記是等號=,即給變量賦值,形式如:a = 3 + 5;
              //則計算等號后面表達式的值,然后再將得到的值賦給變量
              this.getToken();
              //因為加減法的優先級最低,所以計算加減法表達式
              result = this.parseAddOrSub();
              //將表達式的值賦給變量,并存在實例變量vars中
              this.vars[varIndex] = result;
              return result;
             }
            }
            //如果當前標記類型不是變量,或者不是賦值語句,則用加減法計算表達式的值
            return this.parseAddOrSub();
           }
           
           /** 計算加減法表達式 */
           private double parseAddOrSub() throws Exception {
            char op; //運算符
            double result; //結果
            double partialResult; //子表達式的結果
            
            result = this.pareseMulOrDiv(); //用乘除法計算當前表達式的值
            //如果當前標記的第一個字母是加減號,則繼續進行加減運算
            while ((op = this.token.charAt(0)) == '+' || op == '-') {
             this.getToken(); //取下一個標記
             //用乘除法計算當前子表達式的值
             partialResult = this.pareseMulOrDiv();
             switch(op) {
             case '-':
              //如果是減法,則用已處理的子表達式的值減去當前子表達式的值
              result = result - partialResult;
              break;
             case '+':
              //如果是加法,用已處理的子表達式的值加上當前子表達式的值
              result = result + partialResult;
              break;
             }
            }
            return result;
           }
           /**
            * 計算乘除法表達式,包括取模運算
            */
           private double pareseMulOrDiv() throws Exception {
            char op; //運算符
            double result; //結果
            double partialResult; //子表達式結果
            //用指數運算計算當前子表達式的值
            result = this.parseExponent();
            //如果當前標記的第一個字母是乘、除或者取模運算,則繼續進行乘除法運算
            while ((op = this.token.charAt(0)) == '*' || op == '/' || op == '%') {
             this.getToken(); //取下一標記
             //用指數運算計算當前子表達式的值
             partialResult = this.parseExponent();
             switch (op) {
             case '*':
              //如果是乘法,則用已處理子表達式的值乘以當前子表達式的值
              result = result * partialResult;
              break;
             case '/':
              //如果是除法,判斷當前字表達式的值是否為0,如果為0,則拋出被0除異常
              if(partialResult == 0.0) {
               this.handleError(DIVBYZERO_ERROR);
              }
              //除數不為0,則進行除法運算
              result = result / partialResult;
              break;
             case '%':
              //如果是取模運算,也要判斷當前子表達式的值是否為0
              if(partialResult == 0.0) {
               this.handleError(DIVBYZERO_ERROR);
              }
              result = result % partialResult;
              break;
             }
            }
            return result;
           }
           
           /**
            * 計算指數表達式
            */
           private double parseExponent() throws Exception {
            double result; //結果
            double partialResult; //子表達式的值
            double ex; //指數的底數
            int t; //指數的冪
            
            //用一元運算計算當前子表達式的值(底數)
            result = this.parseUnaryOperator();
            //如果當前標記為“^”,則為指數運算
            if (this.token.equals("^")) {
             //獲取下一標記,即獲得指數的冪
             this.getToken();
             partialResult = this.parseExponent();
             ex = result;
             if(partialResult == 0.0) {
              //如果指數的冪為0,則指數的值為1
              result = 1.0;
             } else {
              //否則,指數的值為個數為指數冪的底數相乘的結果
              for (t = (int) partialResult - 1; t > 0; t--) {
               result =result * ex;
              }
             }
            }
            return result;
           }
           
           /**
            * 計算一元運算,+,-,表示正數和負數
            */
           private double parseUnaryOperator() throws Exception{
            double result; //結果
            String op; //運算符
            op = "";
            //如果當前標記類型為分隔符,而且分隔符的值等于+或者-
            if((this.tokenType == DELIMITER_TOKEN) && this.token.equals("+") || this.token.equals("-")) {
             op = this.token;
             this.getToken();
            }
            //用括號運算計算當前子表達式的值
            result = this.parseBracket();
            if(op.equals("-")) {
             //如果運算符為-,則表示負數,將子表達式的值變為負數
             result = -result;
            }
            return result;
           }
           
           /**
            * 計算括號運算
            */
           private double parseBracket() throws Exception {
            double result; //結果
            //如果當前標記為左括號,則表示是一個括號運算
            if (this.token.equals("(")) {
             this.getToken(); //取下一標記
             result = this.parseAddOrSub(); //用加減法運算計算子表達式的值
             //如果當前標記不等于右括號,拋出括號不匹配異常
             if (!this.token.equals(")")) {
              this.handleError(UNBALPARENS_ERROR);
             }
             this.getToken(); //否則取下一個標記
            } else {
             //如果不是左括號,表示不是一個括號運算,則用原子元素運算計算子表達式值
             result = this.parseAtomElement();
            }
            return result;
           }
           
           /**
            * 計算原子元素運算,包括變量和數字
            */
           private double parseAtomElement() throws Exception {
            double result = 0.0; //結果
            
            switch(this.tokenType) {
            case NUMBER_TOKEN:
             //如果當前標記類型為數字
             try {
              //將數字的字符串轉換成數字值
              result = Double.parseDouble(this.token);
             } catch (NumberFormatException exc) {
              this.handleError(SYNTAX_ERROR);
             }
             this.getToken(); //取下一個標記
             break;
            case VARIABLE_TOKEN:
             //如果當前標記類型是變量,則取變量的值
             result = this.findVar(token);
             this.getToken();
             break;
            default:
             this.handleError(SYNTAX_ERROR);
             break;
            }
            return result;
           }
           
           /**
            * 根據變量名獲取變量的值,如果變量名長度大于1,則只取變量的第一個字符
            */
           private double findVar(String vname) throws Exception {
            if (!Character.isLetter(vname.charAt(0))) {
             this.handleError(SYNTAX_ERROR);
             return 0.0;
            }
            //從實例變量數組vars中取出該變量的值
            return vars[Character.toUpperCase(vname.charAt(0)) - 'A'];
           }
           
           /**
            * 回滾,將解析器當前指針往前移到當前標記位置
            */
           private void putBack() {
            if (this.token == EOE) {
             return;
            }
            //解析器當前指針往前移動
            for (int i = 0; i < this.token.length(); i++ ){
             this.expIndex--;
            }
           }
           
           /**
            * 處理異常情況
            */
           private void handleError(int errorType) throws Exception {
            //遇到異常情況時,根據錯誤類型,取得異常提示信息,將提示信息封裝在異常中拋出
            throw new Exception(ERROR_MESSAGES[errorType]);
           }
           
           /**
            * 獲取下一個標記
            */
           private void getToken() {
            //設置初始值
            this.token = "";
            this.tokenType = NONE_TOKEN;
            
            //檢查表達式是否結束,如果解析器當前指針已經到達了字符串長度,
            //則表明表達式已經結束,置當前標記的值為EOE
            if(this.expIndex == this.exp.length()) {
             this.token = EOE;
             return;
            }
            
            //跳過表達式中的空白符
            while (this.expIndex < this.exp.length()
              && Character.isWhitespace(this.exp.charAt(this.expIndex))) {
             ++this.expIndex;
            }
            
            //再次檢查表達式是否結束
            if (this.expIndex == this.exp.length()) {
             this.token = EOE;
             return;
            }
            
            //取得解析器當前指針指向的字符
            char currentChar = this.exp.charAt(this.expIndex);
            //如果當前字符是一個分隔符,則認為這是一個分隔符標記
            //給當前標記和標記類型賦值,并將指針后移
            if(isDelim(currentChar)) {
             this.token += currentChar;
             this.expIndex++;
             this.tokenType = DELIMITER_TOKEN;
            } else if (Character.isLetter(currentChar)) {
             //如果當前字符是一個字母,則認為是一個變量標記
             //將解析器指針往后移,知道遇到一個分隔符,之間的字符都是變量的組成部分
             while(!isDelim(currentChar)) {
              this.token += currentChar;
              this.expIndex++;
              if(this.expIndex >= this.exp.length()) {
               break;
              } else {
               currentChar = this.exp.charAt(this.expIndex);
              }
             }
             this.tokenType = VARIABLE_TOKEN; //設置標記類型為變量
            } else if (Character.isDigit(currentChar)) {
             //如果當前字符是一個數字,則認為當前標記的類型為數字
             //將解析器指針后移,知道遇到一個分隔符,之間的字符都是該數字的組成部分
             while(!isDelim(currentChar)) {
              this.token += currentChar;
              this.expIndex++;
              if (this.expIndex >= this.exp.length()) {
               break;
              } else {
               currentChar = this.exp.charAt(this.expIndex);
              }
             }
             this.tokenType = NUMBER_TOKEN; //設置標記類型為數字
            } else {
             //無法識別的字符,則認為表達式結束
             this.token = EOE;
             return;
            }
           }
           
           /**
            * 判斷一個字符是否為分隔符
            * 表達式中的字符包括:
            * 加“+”、減“-”、乘“*”、除“/”、取模“%”、指數“^”、賦值“=”、左括號“(”、右括號“)”
            */
           private boolean isDelim(char c) {
            if (("+-*/%^=()".indexOf(c) != -1))
             return true;
            return false;
           }
           /**
            * @param args
            */
           public static void main(String[] args) throws Exception{
            ExpressionParser test = new ExpressionParser();
            
            String exp1 = "a = 5.0";
            System.out.println("exp1(\"a = 5.0\") = " + test.evaluate(exp1));
            
            String exp2 = "b = 3.0";
            System.out.println("exp2(\"b = 3.0\") = " + test.evaluate(exp2));
            
            String exp3 = "(a + b) * (a - b)";
            System.out.println("exp3(\"(a + b) * (a - b)\") = " + test.evaluate(exp3));
            
            String exp4 = "3*5-4/2";
            System.out.println("exp4(\"3*5-4/2\") = " + test.evaluate(exp4));
            
            String exp5 = "(4-2) * ((a + b) / (a - b))";
            System.out.println("exp5(\"(4 - 2) * ((a + b) / (a - b))\") = " + test.evaluate(exp5));
            
            String exp6 = "5 % 2";
            System.out.println("exp6(\"5 % 2\") = " + test.evaluate(exp6));
            
            String exp7 = "3^2 * 5 + 4";
            System.out.println("exp7(\"3^2 * 5 + 4\") = " + test.evaluate(exp7));
           }
          }

           

          輸出結果:

          exp1("a = 5.0") = 5.0
          exp2("b = 3.0") = 3.0
          exp3("(a + b) * (a - b)") = 16.0
          exp4("3*5-4/2") = 13.0
          exp5("(4 - 2) * ((a + b) / (a - b))") = 8.0
          exp6("5 % 2") = 1.0
          exp7("3^2 * 5 + 4") = 49.0

          五、實例分析
              表達式的解析,實際就是一個表達式的分解過程。根據分隔符將表達式分成若干段。然后計算每一段的值,最后都會歸結到一個原子表達式。




          posted on 2008-12-29 18:14 .VwV. 閱讀(1158) 評論(2)  編輯  收藏 所屬分類: java

          評論

          # re: java字符串應用之表達式解析器 2011-06-14 15:52 aurora

          為什么不直接引用javascript利用他的計算方法呢(⊙o⊙)?  回復  更多評論   

          # re: java字符串應用之表達式解析器[未登錄] 2011-07-04 16:19 1

          @aurora
          = =!項目需要!  回復  更多評論   

          <2011年6月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          導航

          統計

          常用鏈接

          留言簿

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 富宁县| 呼和浩特市| 隆化县| 荥经县| 大足县| 科技| 宁强县| 都昌县| 博罗县| 岚皋县| 抚宁县| 吕梁市| 阳新县| 娱乐| 肃北| 灯塔市| 霍山县| 淮北市| 开原市| 革吉县| 烟台市| 洮南市| 石河子市| 仲巴县| 遂平县| 沙田区| 象州县| 永康市| 凤翔县| 永嘉县| 永福县| 定远县| 衡东县| 新河县| 宁波市| 南丹县| 金昌市| 故城县| 临猗县| 宝坻区| 滁州市|