【问题标题】:Evaluating arithmetic expressions from string in C++在 C++ 中从字符串计算算术表达式
【发布时间】:2021-08-26 08:52:44
【问题描述】:

我正在寻找一种简单的方法来计算字符串中的简单数学表达式,如下所示:

3*2+4*1+(4+9)*6

我只想要 +* 操作加上 () 符号。并且* 的优先级高于+

【问题讨论】:

    标签: c++


    【解决方案1】:

    大家可以试试:http://partow.net/programming/exprtk/index.html

    1. 很简单
    2. 只需要在您的源代码中包含“exprtk.hpp”。
    3. 您可以动态更改表达式变量的值。
    4. 好的起点:http://partow.net/programming/exprtk/code/exprtk_simple_example_01.cpp

    【讨论】:

    • 这应该是公认的答案! exprtk真的很强大很简单,值得先试试!
    • 在我从链接下载的所有文件中,哪些是在 qt 项目中使用该库的最低要求?
    • @mLstudent33:“只需要在你的源代码中包含“exprtk.hpp”。”
    • 一个缺点是这只适用于 double、float 或任何与标准浮点类型兼容的类型。例如,这不适用于整数。
    • @blue_whale 这不是真的。您可以定义一个包含整数等的自定义数字类型,然后在该数字类型上专门化解析器/表达式类型。 github.com/ArashPartow/exprtk-custom-types
    【解决方案2】:

    我认为您正在寻找一个简单的recursive descent parser

    这是一个非常简单的例子:

    const char * expressionToParse = "3*2+4*1+(4+9)*6";
    
    char peek()
    {
        return *expressionToParse;
    }
    
    char get()
    {
        return *expressionToParse++;
    }
    
    int expression();
    
    int number()
    {
        int result = get() - '0';
        while (peek() >= '0' && peek() <= '9')
        {
            result = 10*result + get() - '0';
        }
        return result;
    }
    
    int factor()
    {
        if (peek() >= '0' && peek() <= '9')
            return number();
        else if (peek() == '(')
        {
            get(); // '('
            int result = expression();
            get(); // ')'
            return result;
        }
        else if (peek() == '-')
        {
            get();
            return -factor();
        }
        return 0; // error
    }
    
    int term()
    {
        int result = factor();
        while (peek() == '*' || peek() == '/')
            if (get() == '*')
                result *= factor();
            else
                result /= factor();
        return result;
    }
    
    int expression()
    {
        int result = term();
        while (peek() == '+' || peek() == '-')
            if (get() == '+')
                result += term();
            else
                result -= term();
        return result;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
        int result = expression();
    
        return 0;
    }
    

    【讨论】:

    • 我不认为递归体面对算术有好处,因为它完全是左递归的。
    【解决方案3】:

    只是添加另一种选择,考虑尝试TinyExpr 解决这个问题。它是开源的,并且自包含在一个源代码文件中。它实际上是用 C 编写的,但根据我的经验,它可以像 C++ 一样干净地编译。

    从上面解决您的示例表达式很简单:

    #include "tinyexpr.h"
    #include <stdio.h>
    
    int main()
    {
        double answer = te_interp("3*2+4*1+(4+9)*6", 0);
        printf("Answer is %f\n", answer);
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      所以我正在寻找这个问题的答案。我正在尝试创建自己的编程语言。对于数学表达式,我需要那个函数。

      好吧,我给你。随心所欲地使用它。

      /* Code here before is useless now */
      

      这是一种很长而且可能是一种低效的方式来完成这样的任务。但它可以完成工作,所以去做吧。很快我就计划添加变量支持。但你也可以做到,这很容易(我想:P)。

      编辑:我刚刚整理了这个功能,现在它就像魔法一样工作 XD..

      using namespace std;
      
      double eval(string expr)
      {
          string xxx; // Get Rid of Spaces
          for (int i = 0; i < expr.length(); i++)
          {
              if (expr[i] != ' ')
              {
                  xxx += expr[i];
              }
          }
      
          string tok = ""; // Do parantheses first
          for (int i = 0; i < xxx.length(); i++)
          {
              if (xxx[i] == '(')
              {
                  int iter = 1;
                  string token;
                  i++;
                  while (true)
                  {
                      if (xxx[i] == '(')
                      {
                          iter++;
                      } else if (xxx[i] == ')')
                      {
                          iter--;
                          if (iter == 0)
                          {
                              i++;
                              break;
                          }
                      }
                      token += xxx[i];
                      i++;
                  }
                  //cout << "(" << token << ")" << " == " << to_string(eval(token)) <<  endl;
                  tok += to_string(eval(token));
              }
              tok += xxx[i];
          }
      
          for (int i = 0; i < tok.length(); i++)
          {
              if (tok[i] == '+')
              {
                  //cout << tok.substr(0, i) + " + " +  tok.substr(i+1, tok.length()-i-1) << " == " << eval(tok.substr(0, i)) + eval(tok.substr(i+1, tok.length()-i-1)) << endl;
                  return eval(tok.substr(0, i)) + eval(tok.substr(i+1, tok.length()-i-1));
              } else if (tok[i] == '-')
              {
                  //cout << tok.substr(0, i) + " - " +  tok.substr(i+1, tok.length()-i-1) << " == " << eval(tok.substr(0, i)) - eval(tok.substr(i+1, tok.length()-i-1)) << endl;
                  return eval(tok.substr(0, i)) - eval(tok.substr(i+1, tok.length()-i-1));
              }
          }
      
          for (int i = 0; i < tok.length(); i++)
          {
              if (tok[i] == '*')
              {
                  //cout << tok.substr(0, i) + " * " +  tok.substr(i+1, tok.length()-i-1) << " == " << eval(tok.substr(0, i)) * eval(tok.substr(i+1, tok.length()-i-1)) << endl;
                  return eval(tok.substr(0, i)) * eval(tok.substr(i+1, tok.length()-i-1));
              } else if (tok[i] == '/')
              {
                  //cout << tok.substr(0, i) + " / " +  tok.substr(i+1, tok.length()-i-1) << " == " << eval(tok.substr(0, i)) / eval(tok.substr(i+1, tok.length()-i-1)) << endl;
                  return eval(tok.substr(0, i)) / eval(tok.substr(i+1, tok.length()-i-1));
              }
          }
      
          //cout << stod(tok.c_str()) << endl;
          return stod(tok.c_str()); // Return the value...
      }
      

      【讨论】:

      • 此代码不适用于负数。这会破坏操作顺序和功能。我通过添加一个简单的规则来修复这个else if (tok[i] == '-') { if (tok.substr(0, i).length() != 0 &amp;&amp; tok[i - 1] != '*' &amp;&amp; tok[i - 1] != '/') return eval(tok.substr(0, i)) + eval("-" + tok.substr(i + 1, tok.length() - i - 1)); } 并将 - 更改为 + 同时将 - 传递给其余的评估,这样操作顺序就不会中断。
      【解决方案5】:

      在为类似任务搜索库时,我发现了libmatheval。似乎是一件正当的事情。不幸的是,GPL,这对我来说是不可接受的。

      【讨论】:

        【解决方案6】:

        我用 C# 编写了一个非常简单的表达式求值器(使其符合 C++ 标准所需的最小更改)。它基于表达式树的构建方法,只是该树并未实际构建,而是所有节点都在原地评估。

        你可以在这个地址找到它:Simple Arithmetic Expression Evaluator

        【讨论】:

          【解决方案7】:

          将您的中缀表达式转换为后缀表达式。然后评估。

          后缀可能看起来像3 2 * 4 1 * + 4 9 + 6 * +

          使用堆栈很容易评估。

          【讨论】:

            猜你喜欢
            • 2011-06-04
            • 1970-01-01
            • 2013-04-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多