在 C++ 中从字符串计算算术表达式

Posted

技术标签:

【中文标题】在 C++ 中从字符串计算算术表达式【英文标题】:Evaluating arithmetic expressions from string in C++ 【发布时间】:2012-02-17 13:49:26 【问题描述】:

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

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

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

【问题讨论】:

【参考方案1】:

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

    很简单 只需要在您的源代码中包含“exprtk.hpp”。 您可以动态更改表达式变量的值。 好的起点: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 * +

使用堆栈很容易评估。

【讨论】:

以上是关于在 C++ 中从字符串计算算术表达式的主要内容,如果未能解决你的问题,请参考以下文章

C++或C语言如何将字符串转化为数学表达式

php字符串算术表达式计算

算法_计算输入的算术表达式的值.

如何让 GCC (C++) 停止简化算术表达式?

判断C语言算术表达式的合法性

C++:算术表达式求值