现在的位置: 首页 > 综合 > 正文

一个简单的表达式计算器的C++实现

2013年10月12日 ⁄ 综合 ⁄ 共 8529字 ⁄ 字号 评论关闭

这个程序的编写本质上主要是栈的应用。我的程序主要分为2部分。第一部分为把输入的中缀表达式转换为后缀表达式,第二部分为计算后缀表达式。2个部分都用一个栈来辅助操作并检查表达式的是否合法。

1)、中缀表达式转换为后缀表达式:

函数int infix_to_postfix(const string& infix, string& postfix);执行中缀表达式转换为后缀表达式的计算。它使用一个栈来存储遇到的操作符和左括号,遇到操作数则直接输出。

操作符在入栈的时候要比较栈顶符号和待入栈符号的优先级,优先级高的操作符意味着它要先计算,所以要先输出。

当优先级相同时,(a)如果是待入栈操作符是左结合性,则先输出栈顶的那个操作符,这样直到栈顶符号的优先级小于当前待入栈的符号为止。然后把待入栈的操作符压入栈中;(b)如果待入栈操作符是右结合性,则要把操作符直接入栈。比如在本程序中,我们对^预算符就是这么处理的。

在转换过程中,还有一个比较特殊的符号,那就是括号。括号在中缀表达式中是用来确定计算先后顺序的,但是在后缀表达式中,它就不再需要了。计算的顺序是通过后缀表达式的性质来保证的。把一对括号中的中缀表达式转换为后缀表达式,然后把括号直接去掉就可以了。所以对括号的处理就是:遇到左括号,和其他的运算符一样,把比它优先级还要高的运算符弹出来(但是遇到左括号时就要停止了),然后把左括号入栈。遇到右括号时,则出栈,即把一对括号之间的中缀表达式已经转为后缀表达式了。当左括号为栈顶的时候,停止出栈循环,然后弹出栈顶的左括号。

 

2)计算后缀表达式:

       int compute_postfix(const string& postfix_expr, double& result);用来计算前面产生的后缀表达式。这个过程比较简单,遇到操作数时,直接入栈;由于在后缀表达式中,操作符都是在它的操作数后面的,所以遇到操作符时,就从栈中取出这个操作符需要的操作数然,进行必要的检查后,就可以进行计算了。计算结果再次压入栈中,供后面使用。当后缀表达式处理完毕后,最后的结果就是栈顶了。

 

上面介绍的两个部分都用到了int getToken(const string& expr, string& token, size_t& idx);函数。这个函数的作用是:把输入的表达式切分为一个个的操作符或者操作数,供使用。

 

代码如下:

 

  1. #define _CRT_SECURE_NO_WARNINGS // for MS crt.
  2. #include <fstream>
  3. #include <stack>
  4. #include <string>
  5. #include <iomanip>
  6. #include <cmath>
  7. using namespace std;
  8. bool isLetter(char c);
  9. bool isOperator(char c);
  10. bool isBracket(char c);
  11. int getToken(const string& expr, string& token, size_t& idx);
  12. int getOperatorPriority(const string& op);
  13. int infix_to_postfix(const string& infix, string& postfix);
  14. int compute_postfix(const string& postfix_expr, double& result);
  15. int main()
  16. {
  17.     ifstream cin("input.txt");          // input
  18.     ofstream cout("output.txt");        // output
  19.     string infix_expr, postfix_expr;
  20.     double result = 0.0;
  21.     
  22.     while (getline(cin, infix_expr)) {
  23.         if (infix_to_postfix(infix_expr, postfix_expr)) {
  24.             if (compute_postfix(postfix_expr, result)) {
  25.                 cout << fixed << setprecision(2) << result << endl;
  26.                 continue ; // have no error, so next expr.
  27.             }
  28.         }
  29.     
  30.         cout << "ERROR IN INFIX NOTATION" << endl;
  31.     }
  32.     return 0;
  33. }
  34. int infix_to_postfix(const string& infix_expr, string& postfix_expr)
  35. {
  36.     string token;
  37.     size_t idx = 0;
  38.     stack<string> stk;
  39.     int balance = 0; // use to check the bracket's balance.
  40.     postfix_expr.clear();
  41.     while (getToken(infix_expr, token, idx)) {
  42.         switch (token[0]) {
  43.             /* If we see '+','-','*','/','%','^','(' , then we pop entries from
  44.              * the stack until we find an entry of lower priority
  45.              * One exception is that we never remove a '(' from the stack except when processing a ')'.*/
  46.             case '+':
  47.             case '-':
  48.             case '*':
  49.             case '/':
  50.             case '%':
  51.             case '(':
  52.                 if (token[0] == '(')
  53.                     ++balance;
  54.                 while (!stk.empty() && getOperatorPriority(stk.top()) >= getOperatorPriority(token) && stk.top() != "(") {
  55.                     postfix_expr += stk.top();
  56.                     stk.pop();
  57.                     postfix_expr += " ";
  58.                 }
  59.                 stk.push(token);
  60.                 break;
  61.             /* right association, handle it specifically! */
  62.             case '^':
  63.                 while (!stk.empty() && getOperatorPriority(stk.top()) > getOperatorPriority(token) && stk.top() != "(") {
  64.                     postfix_expr += stk.top();
  65.                     stk.pop();
  66.                     postfix_expr += " ";
  67.                 }
  68.                 stk.push(token); // later come, early out.(right association)
  69.                 break;
  70.             /* If we see a ')', then we pop the stack, writing symbols until we encounter
  71.              * a (corresponding) left parenthesis, which is poped but not output. */
  72.             case ')':
  73.                 --balance;
  74.                 while (!stk.empty() && stk.top() != "(") {
  75.                     postfix_expr += stk.top();
  76.                     stk.pop();
  77.                     postfix_expr += " ";
  78.                 }
  79.                 stk.pop();
  80.                 break;
  81.             default:
  82.                 postfix_expr += token;
  83.                 postfix_expr += " ";
  84.                 break;
  85.         }
  86.     }
  87.     while (!stk.empty()) {
  88.         postfix_expr += " ";
  89.         postfix_expr += stk.top();
  90.         stk.pop();
  91.     }
  92.     if (balance != 0) {
  93.         return 0;
  94.     }
  95.     return 1;
  96. }
  97. int compute_postfix(const string& postfix_expr, double& result)
  98. {
  99.     stack<string> stk;
  100.     string token;
  101.     size_t idx = 0;
  102.     double operand1 = 0.0, operand2 = 0.0;
  103.     char buf[128] = {0};
  104.     int resStatus = 0;
  105.     while (getToken(postfix_expr, token, idx)) {
  106.         memset(buf, 0, 128 * sizeof(char));
  107.         switch (token[0]) {
  108.             case '+':
  109.             case '-':
  110.             case '*':
  111.             case '/':
  112.             case '%':
  113.             case '^':
  114.                 // firstly, get two operands to compute.
  115.                 operand1 = atof(stk.top().c_str());
  116.                 stk.pop();
  117.                 operand2 = atof(stk.top().c_str());
  118.                 stk.pop();
  119.                 switch (token[0]) {
  120.                     case '+':
  121.                         sprintf(buf, "%f", operand1 + operand2);
  122.                         break;
  123.                     case '-':
  124.                         sprintf(buf, "%f", operand2 - operand1);
  125.                         break;
  126.                     case '*':
  127.                         sprintf(buf, "%f", operand1 * operand2);
  128.                         break;
  129.                     case '/':
  130.                         if (!operand1) {
  131.                             resStatus = 0;
  132.                             goto Exit;
  133.                         }
  134.                         sprintf(buf, "%f", operand2 / operand1);
  135.                         break;
  136.                     case '%':
  137.                         // if operand is a float number, then error!
  138.                         if ((int)operand1 != operand1 || (int)operand2 != operand2 || !operand1) {
  139.                             resStatus = 0;
  140.                             goto Exit;
  141.                         }
  142.                         // care: the format should be "%d".
  143.                         sprintf(buf, "%d", (int)operand2 % (int)operand1);
  144.                         break;
  145.                     case '^':
  146.                         if (operand2 <= 0) {
  147.                             resStatus = 0;
  148.                             goto Exit;                      
  149.                         }
  150.                         sprintf(buf, "%f", pow(operand2, operand1));
  151.                         break;
  152.                 }
  153.                 stk.push(string(buf)); 
  154.                 break;
  155.             default:
  156.                 stk.push(token); // numbers push into the stack directly.
  157.                 break;
  158.         }
  159.     }
  160.     // now the number in the stack is the result.
  161.     result = atof(stk.top().c_str());
  162.     resStatus = 1;
  163. Exit:
  164.     return resStatus;
  165. }
  166. int getOperatorPriority(const string& op)
  167. {
  168.     int priority = -1;
  169.     switch (op[0])
  170.     {
  171.         case '(':
  172.             priority = 4;
  173.             break;
  174.         case '+':
  175.         case '-':
  176.             priority = 1;
  177.             break;
  178.         case '*':
  179.         case '/':
  180.         case '%':
  181.             priority = 2;
  182.             break;
  183.         case '^':
  184.             priority = 3;
  185.             break;
  186.     }
  187.     return priority;
  188. }
  189. int getToken(const string& expr, string& token, size_t& idx)
  190. {
  191.     char curChar = 0;
  192.     bool inTok = false ;
  193.     int res = 0;
  194.     token.clear();
  195.     size_t len = expr.length();
  196.     while (idx < len) {
  197.         curChar = expr[idx++];
  198.         if (isspace(curChar) || isOperator(curChar) || isBracket(curChar)) {
  199.             if (!inTok && (isOperator(curChar) || isBracket(curChar))) {
  200.                 token += curChar;
  201.                 res = 1; // token is a operatro or bracket, return token immediately.
  202.                 break;
  203.             }
  204.             if (inTok) {
  205.                 if (isOperator(curChar) || isBracket(curChar))
  206.                     --idx; // back input pointer and get the operator or bracket when we call getToken() next time.
  207.                 res = 1;
  208.                 break;
  209.             }
  210.             inTok = false;
  211.         }  else if (isLetter(curChar)) { // letter is illegal.
  212.             res = 0;
  213.             break;
  214.         }  else if (!inTok) { // begin a number.
  215.             inTok = true;
  216.             token += curChar;
  217.         } else if (inTok) {
  218.             token += curChar;
  219.         }
  220.     }
  221.     if (!token.empty())
  222.         res = 1;
  223.     return res;
  224. }
  225. bool isLetter(char c)
  226. {
  227.     if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
  228.         return true;
  229.     return false;
  230. }
  231. bool isOperator(char c)
  232. {
  233.     if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^')
  234.         return true ;
  235.     return false ;
  236. }
  237. bool isBracket(char c)
  238. {
  239.     if (c == '(' || c == ')')
  240.         return true;
  241.     return false;
  242. }

 

 

 

抱歉!评论已关闭.