四則混合運算表達式分析程序----C#遞歸分析版

 

源碼下載: http://www.gzqzyz.cn/UploadFiles/2008-8/expressionanalyzer.rar

# Token.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace OOHacker.ExpressionAnalyzer
  5. {
  6.   //===========================================================================
  7.   // 抽象標記
  8.   //===========================================================================
  9.   public abstract class Token
  10.   {
  11.     public abstract object Value { get;}
  12.     public override string ToString()
  13.     {
  14.       return Value.ToString();
  15.     }
  16.   }
  17.   //===========================================================================
  18.   // 左括號
  19.   //===========================================================================
  20.   public class LP : Token
  21.   {
  22.     public override object Value
  23.     {
  24.       get
  25.       {
  26.         return '(';
  27.       }
  28.     }
  29.   }
  30.   //===========================================================================
  31.   // 右括號
  32.   //===========================================================================
  33.   public class RP : Token
  34.   {
  35.     public override object Value
  36.     {
  37.       get
  38.       {
  39.         return ')';
  40.       }
  41.     }
  42.   }
  43.   //===========================================================================
  44.   // 數值
  45.   //===========================================================================
  46.   public class Number : Token
  47.   {
  48.     private double val;
  49.     public Number(double value)
  50.     {
  51.       val = value;
  52.     }
  53.     public override object Value
  54.     {
  55.       get
  56.       {
  57.         return val;
  58.       }
  59.     }
  60.   }
  61.   //===========================================================================
  62.   // 操作符
  63.   //===========================================================================
  64.   public class Oper : Token
  65.   {
  66.     private char op;
  67.     public Oper(char oper)
  68.     {
  69.       op = oper;
  70.     }
  71.     public override object Value
  72.     {
  73.       get
  74.       {
  75.         return op;
  76.       }
  77.     }
  78.   }
  79. }

# GrammarAnalyzer.CS

  1. using System;
  2. using System.Text;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. namespace OOHacker.ExpressionAnalyzer
  6. {
  7.   public class GrammerAnalyzer
  8.   {
  9.     //=========================================================================
  10.     // 構造函數: 
  11.     //   exp: 要分析的表達式
  12.     //=========================================================================
  13.     public GrammerAnalyzer(string exp)
  14.     {
  15.       expression = exp;
  16.       index = 0;
  17.       tokens = new List<Token>();
  18.     }
  19.     //=========================================================================
  20.     // 表達式詞法分析
  21.     //=========================================================================
  22.     public void Analyze()
  23.     {
  24.       tokens.Clear();
  25.       index = 0;
  26.       Token tok = null;
  27.       while ((tok = GetToken()) != null)
  28.       {
  29.         tokens.Add(tok);
  30.       }
  31.     }
  32.     //=========================================================================
  33.     // 獲取標記列表
  34.     //=========================================================================
  35.     public Token[] TokenList
  36.     {
  37.       get { return tokens.ToArray(); }
  38.     }
  39.     //=========================================================================
  40.     // 分析後讀取一個標記
  41.     //=========================================================================
  42.     private Token GetToken()
  43.     {
  44.       Token token = null;
  45.       // 忽略空白字符
  46.       while (HasChar && char.IsWhiteSpace(CurrentChar)) MoveNext();
  47.       if (!HasChar) return null;
  48.       if (CurrentChar == '(')             // 左括號
  49.       {
  50.         token = new LP();
  51.       }
  52.       else if (CurrentChar == ')')        // 右括號
  53.       {
  54.         token = new RP();
  55.       }
  56.       else if (IsOper(CurrentChar))       // 操作符
  57.       {   
  58.         token = new Oper(CurrentChar);
  59.       }
  60.       else if (char.IsDigit(CurrentChar)) // 數字
  61.       {
  62.         bool dotFound = false;
  63.         StringBuilder buf = new StringBuilder();
  64.         do
  65.         {
  66.           char c = CurrentChar;
  67.           if (dotFound && c == '.')
  68.           {
  69.             throw new Exception("無效的數字!");
  70.           }
  71.           buf.Append(c);
  72.           MoveNext();
  73.         } while (HasChar && (char.IsDigit(CurrentChar) || CurrentChar == '.'));
  74.         MovePrev(); // 回溯一次
  75.         token = new Number(Convert.ToDouble(buf.ToString()));
  76.       }
  77.       else
  78.       {
  79.         throw new Exception("表達式中含有無效字符:'" + CurrentChar + "'!");
  80.       }
  81.       MoveNext();
  82.       return token;
  83.     }
  84.     //=========================================================================
  85.     // 是否操作符
  86.     //=========================================================================
  87.     private bool IsOper(char c)
  88.     {
  89.       string opers = "+-*/";
  90.       return opers.IndexOf(c) != -1;
  91.     }
  92.     //=========================================================================
  93.     // 是否還有可讀字符
  94.     //=========================================================================
  95.     private bool HasChar
  96.     {
  97.       get { return index < expression.Length; }
  98.     }
  99.     //=========================================================================
  100.     // 當前字符
  101.     //=========================================================================
  102.     private char CurrentChar
  103.     {
  104.       get { return expression[index]; }
  105.     }
  106.     //=========================================================================
  107.     // 轉到下一字符
  108.     //=========================================================================
  109.     private void MoveNext()
  110.     {
  111.       ++index;
  112.     }
  113.     //=========================================================================
  114.     // 轉到上一字符
  115.     //=========================================================================
  116.     private void MovePrev()
  117.     {
  118.       --index;
  119.     }
  120.     private int index;
  121.     private string expression;
  122.     private List<Token> tokens;
  123.   }
  124. }

 

# SyntaxAnalyzer.CS

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace OOHacker.ExpressionAnalyzer
  5. {
  6.   //===========================================================================
  7.   // 語法樹結點
  8.   //===========================================================================
  9.   public class SyntaxTreeNode
  10.   {
  11.     public SyntaxTreeNode(Token tok,
  12.       SyntaxTreeNode lc,
  13.       SyntaxTreeNode rc,
  14.       int nest)
  15.     {
  16.       token = tok;
  17.       lchild = lc;
  18.       rchild = rc;
  19.       this.nest = nest;
  20.     }
  21.     //=========================================================================
  22.     // 標記
  23.     //=========================================================================
  24.     public Token Token
  25.     {
  26.       set { token = value; }
  27.       get { return token; }
  28.     }
  29.     //=========================================================================
  30.     // 左孩子
  31.     //=========================================================================
  32.     public SyntaxTreeNode LChild
  33.     {
  34.       set { lchild = value; }
  35.       get { return lchild; }
  36.     }
  37.     //=========================================================================
  38.     // 右孩子
  39.     //=========================================================================
  40.     public SyntaxTreeNode RChild
  41.     {
  42.       set { rchild = value; }
  43.       get { return rchild; }
  44.     }
  45.     //=========================================================================
  46.     // 嵌套層數
  47.     //=========================================================================
  48.     public int Nest
  49.     {
  50.       set { nest = value; }
  51.       get { return nest; }
  52.     }
  53.     private Token token;
  54.     private SyntaxTreeNode lchild;
  55.     private SyntaxTreeNode rchild;
  56.     private int nest;
  57.   }
  58.   //===========================================================================
  59.   // 語法分析器
  60.   //===========================================================================
  61.   public class SyntaxAnalyzer
  62.   {
  63.     public SyntaxAnalyzer(Token[] toks)
  64.     {
  65.       tokens = toks;
  66.       stack = new Stack<SyntaxTreeNode>();
  67.       tree = null;
  68.       index = 0;
  69.     }
  70.     //=========================================================================
  71.     // 獲取語法樹
  72.     //=========================================================================  
  73.     public SyntaxTreeNode SyntaxTree
  74.     {
  75.       get { return tree; }
  76.     }
  77.     //=========================================================================
  78.     // 分析表達式
  79.     //=========================================================================  
  80.     public void Analyze()
  81.     {
  82.       // 還原初始狀態
  83.       stack.Clear();
  84.       index = 0;
  85.       tree = null;
  86.       nest = 0;
  87.       if (!HasToken) return;
  88.       // 表達式必須以數值或左括號開頭
  89.       if (CurrentToken is Number)
  90.       {
  91.         N(ref tree);
  92.       }
  93.       else if (CurrentToken is LP)
  94.       {
  95.         Lp(ref tree);
  96.       }
  97.       else
  98.       {
  99.         throw new Exception("無效表達式! 表達式只能數值或左括號開頭!");
  100.       }
  101.       if (stack.Count != 0)
  102.       {
  103.         throw new Exception("無效表達式! 左右括號不配對");
  104.       }
  105.     }
  106.     //=========================================================================
  107.     // 數值標記分析
  108.     //=========================================================================  
  109.     private void N(ref SyntaxTreeNode curr)
  110.     {
  111.       SyntaxTreeNode node = new SyntaxTreeNode(CurrentToken, 
  112.         null
  113.         null
  114.         nest);
  115.       if (curr == null)
  116.       {
  117.         curr = node;
  118.       }
  119.       else
  120.       {
  121.         if (curr.RChild != null)
  122.         {
  123.           curr.RChild.RChild = node;
  124.         }
  125.         else
  126.         {
  127.           curr.RChild = node;
  128.         }
  129.       }
  130.       MoveNext();
  131.       // 數值後的標記必須是操作符或右括號
  132.       if (!HasToken) return;
  133.       if (CurrentToken is Oper)
  134.       {
  135.         Op(ref curr);
  136.       }
  137.       else if (CurrentToken is RP)
  138.       {
  139.         Rp(ref curr);
  140.       }
  141.       else
  142.       {
  143.         throw new Exception("無效表達式! 數值之後發現標記:'" 
  144.           + CurrentToken.Value + "'!");
  145.       }
  146.     }
  147.     //=========================================================================
  148.     // 左括號分析
  149.     //=========================================================================
  150.     private void Lp(ref SyntaxTreeNode curr)
  151.     {
  152.       stack.Push(curr);     // 壓入括號外分析樹
  153.       curr = null;          // 清空分析樹
  154.       ++nest;               // 嵌套層增加
  155.       MoveNext();
  156.       if (!HasToken) return;
  157.       // 左括號後必須是數值或左括號
  158.       if (CurrentToken is Number)
  159.       {
  160.         N(ref curr);
  161.       }
  162.       else if (CurrentToken is LP)
  163.       {
  164.         Lp(ref curr);
  165.       }
  166.       else
  167.       {
  168.         throw new Exception("無效表達式! 左括號之後發現標記:'" 
  169.           + CurrentToken.Value + "'!");
  170.       }
  171.     }
  172.     //=========================================================================
  173.     // 右括號分析
  174.     //=========================================================================
  175.     private void Rp(ref SyntaxTreeNode curr)
  176.     {
  177.       if (stack.Count == 0)
  178.       {
  179.         throw new Exception("無效表達式! 左右括號不配對!");
  180.       }
  181.       --nest; // 嵌套層遞減
  182.       SyntaxTreeNode node = stack.Pop();
  183.       if (node != null)
  184.       {
  185.         // 將括號內表達式樹作爲子樹掛到括號外表達式樹上
  186.         if (node.RChild != null)
  187.         {
  188.           node.RChild.RChild = curr;
  189.           curr = node;
  190.         }
  191.         else
  192.         {
  193.           node.RChild = curr;
  194.           curr = node;
  195.         }
  196.       }
  197.       MoveNext();
  198.       if (!HasToken) return;
  199.       // 右括號後面必須是操作符或右括號
  200.       if (CurrentToken is Oper)
  201.       {
  202.         Op(ref curr);
  203.       }
  204.       else if (CurrentToken is RP)
  205.       {
  206.         Rp(ref curr);
  207.       }
  208.       else
  209.       {
  210.         throw new Exception("無效表達式! 右括號之後發現標記:'" 
  211.           + CurrentToken.Value + "'!");
  212.       }
  213.     }
  214.     //=========================================================================
  215.     // 操作符分析
  216.     //=========================================================================
  217.     private void Op(ref SyntaxTreeNode curr)
  218.     {
  219.       SyntaxTreeNode node = new SyntaxTreeNode(CurrentToken, nullnull, nest);
  220.       // 只有位於同一嵌套層中的操作符有優先級可比性
  221.       if ((curr.Token is Oper) 
  222.         && (node.Nest == curr.Nest) 
  223.         && IsPrior((char)CurrentToken.Value, (char)curr.Token.Value) > 0)
  224.       {
  225.         node.LChild = curr.RChild;
  226.         curr.RChild = node;
  227.       }
  228.       else
  229.       {
  230.         node.LChild = curr;
  231.         curr = node;
  232.       }
  233.       MoveNext();
  234.       // 操作符之後必須是數或左括號
  235.       if (CurrentToken is Number)
  236.       {
  237.         N(ref curr);
  238.       }
  239.       else if (CurrentToken is LP)
  240.       {
  241.         Lp(ref curr);
  242.       }
  243.       else
  244.       {
  245.         throw new Exception("無效表達式! 操作符之後發現標記:'" 
  246.           + CurrentToken.Value + "'!");
  247.       }
  248.     }
  249.     //=========================================================================
  250.     // 比較操作符優先級
  251.     //=========================================================================
  252.     private int IsPrior(char op1, char op2)
  253.     {
  254.       int rslt = 0;
  255.       if (op1 == '+' || op1 == '-')
  256.       {
  257.         if (op2 == '*' || op2 == '/')
  258.           rslt = -1;
  259.       }
  260.       else if (op1 == '*' || op1 == '/')
  261.       {
  262.         if (op2 == '+' || op2 == '-')
  263.           rslt = 1;
  264.       }
  265.       return rslt;
  266.     }
  267.     //=========================================================================
  268.     // 是否還有標記可讀
  269.     //=========================================================================
  270.     private bool HasToken
  271.     {
  272.       get { return index < tokens.Length; }
  273.     }
  274.     //=========================================================================
  275.     // 轉到下一標記
  276.     //=========================================================================
  277.     private void MoveNext()
  278.     {
  279.       ++index;
  280.     }
  281.     //=========================================================================
  282.     // 當前標記
  283.     //=========================================================================  
  284.     private Token CurrentToken
  285.     {
  286.       get { return (Token)tokens[index]; }
  287.     }
  288.     private int index;
  289.     private int nest;
  290.     private SyntaxTreeNode tree;
  291.     private Token[] tokens;
  292.     private Stack<SyntaxTreeNode> stack;
  293.   }
  294. }

 

# Calculator.CS

  1. using System;
  2. using System.Text;
  3. using System.IO;
  4. namespace OOHacker.ExpressionAnalyzer
  5. {
  6.   //===========================================================================
  7.   // 表達式計算器
  8.   //===========================================================================
  9.   public class Calculator
  10.   {
  11.     //=========================================================================
  12.     // 構造函數
  13.     //   tree: 表達式語法樹, 由SyntaxAnalyzer創建;
  14.     //=========================================================================
  15.     public Calculator(SyntaxTreeNode tree)
  16.     {
  17.       this.tree = tree;
  18.       log = new StringBuilder();
  19.     }
  20.     //=========================================================================
  21.     // 計算表達式值
  22.     public double Calc()
  23.     {
  24.       if (tree == null)
  25.       {
  26.         throw new Exception("計算樹爲空!");
  27.       }
  28.       log.Remove(0, log.Length);
  29.       return Travel(tree);
  30.     }
  31.     //=========================================================================
  32.     // 求解過程日誌
  33.     //=========================================================================
  34.     public string Log
  35.     {
  36.       get { return log.ToString(); }
  37.     }
  38.     //=========================================================================
  39.     // 遍歷表達式分析樹,求取結果
  40.     //=========================================================================
  41.     private double Travel(SyntaxTreeNode tree)
  42.     {
  43.       if (tree.Token is Number)
  44.       {
  45.         return (double)tree.Token.Value;
  46.       }
  47.       double lsh = Travel(tree.LChild);
  48.       double rsh = Travel(tree.RChild);
  49.       return Op((char)tree.Token.Value, lsh, rsh);
  50.     }
  51.     //=========================================================================
  52.     // 實際計算操作
  53.     //=========================================================================
  54.     private double Op(char op, double lsh, double rsh)
  55.     {
  56.       double rslt = 0;
  57.       switch (op)
  58.       {
  59.         case '+':
  60.           rslt = lsh + rsh;
  61.           break;
  62.         case '-':
  63.           rslt = lsh - rsh;
  64.           break;
  65.         case '*':
  66.           rslt = lsh * rsh;
  67.           break;
  68.         case '/':
  69.           if (rsh == 0)
  70.           {
  71.             throw new Exception("除0溢出!");
  72.           }
  73.           rslt = lsh / rsh;
  74.           break;
  75.         default:
  76.           throw new Exception("未能識別的操作類型!");
  77.       }
  78.       
  79.       log.AppendFormat("{0,5}{1,5}{2,5}={3,5}/n", lsh, op, rsh, rslt);
  80.       return rslt;
  81.     }
  82.     private SyntaxTreeNode tree;
  83.     private StringBuilder log;
  84.   }
  85. }

 

# 主程序

  1. using System;
  2. using System.Text;
  3. using System.Collections;
  4. using OOHacker.ExpressionAnalyzer;
  5. class Program
  6. {
  7.   static void Main(string[] args)
  8.   {
  9.     string expression;
  10.     Console.Write("請輸入表達式:");
  11.     expression = Console.ReadLine();
  12.     try
  13.     {
  14.       GrammerAnalyzer ga = new GrammerAnalyzer(expression);
  15.       ga.Analyze();
  16.       Token[] toks = ga.TokenList;
  17.       for (int i = 0; i < toks.Length; ++i)
  18.       {
  19.         Console.Write(toks[i]);
  20.       }
  21.       SyntaxAnalyzer sa = new SyntaxAnalyzer(toks);
  22.       sa.Analyze();
  23.       Calculator calc = new Calculator(sa.SyntaxTree);
  24.       double value = calc.Calc();
  25.       Console.Write("={0}", value);
  26.       Console.WriteLine("/n解析過程:/n{0}", calc.Log);
  27.     }
  28.     catch (Exception e)
  29.     {
  30.       Console.WriteLine("錯誤:" + e.Message);
  31.     }
  32.     Console.Read();
  33.   }
  34. }

# 測試:

請輸入表達式:(1*2+(3-5)*7)/22+(1-2*3)*5
(1*2+(3-5)*7)/22+(1-2*3)*5=-25.5454545454545
解析過程:
    1    *    2=    2
    3    -    5=   -2
   -2    *    7=  -14
    2    +  -14=  -12
  -12    /   22=-0.545454545454545
    2    *    3=    6
    1    -    6=   -5
   -5    *    5=  -25
-0.545454545454545    +  -25=-25.5454545454545


請輸入表達式:(1+2+3*(1+2+3)/6)*5*(1+2+3)*(1+1)
(1+2+3*(1+2+3)/6)*5*(1+2+3)*(1+1)=360
解析過程:
    1    +    2=    3
    1    +    2=    3
    3    +    3=    6
    3    *    6=   18
   18    /    6=    3
    3    +    3=    6
    6    *    5=   30
    1    +    2=    3
    3    +    3=    6
   30    *    6=  180
    1    +    1=    2
  180    *    2=  360


請輸入表達式:(3+2*(9/3))*10-5/8-3*3*(1+2+3+(1+2+3))
(3+2*(9/3))*10-5/8-3*3*(1+2+3+(1+2+3))=-18.625
解析過程:
    9    /    3=    3
    2    *    3=    6
    3    +    6=    9
    9    *   10=   90
    5    /    8=0.625
   90    -0.625=89.375
    3    *    3=    9
    1    +    2=    3
    3    +    3=    6
    1    +    2=    3
    3    +    3=    6
    6    +    6=   12
    9    *   12=  108
89.375    -  108=-18.625

 

請輸入表達式:1+(2*3)/6*(3+5)
1+(2*3)/6*(3+5)=9
解析過程:
    2    *    3=    6
    6    /    6=    1
    3    +    5=    8
    1    *    8=    8
    1    +    8=    9


請輸入表達式:3+2*(3+1)*5-1
3+2*(3+1)*5-1=42
解析過程:
    3    +    1=    4
    2    *    4=    8
    8    *    5=   40
    3    +   40=   43
   43    -    1=   42

 

,
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章