在 Qt 中计算数学表达式

Evaluate a math expression in Qt

本文关键字:表达式 计算 Qt      更新时间:2023-10-16

我正在尝试创建一个Qt应用程序,我需要一个数学表达式计算器来评估这样的东西,例如(4 + 5(* 2-9/3。我将这个库(http://www.partow.net/programming/exprtk/(的.hpp文件包含在Qt Creator中的项目中,并尝试启动以下代码示例:

#include <cstdio>
#include <string>
#include "exprtk.hpp"
int main()
{
   typedef exprtk::expression<double> expression_t;
   typedef exprtk::parser<double>         parser_t;
   std::string expression_string = "3 + sqrt(5) + pow(3,2) + log(5)";
   expression_t expression;
   parser_t parser;
   if (parser.compile(expression_string,expression))
   {
     double result = expression.value();
     printf("Result: %19.15n",result);
   }
   else
     printf("Error in expressionn.");
   return 0;
}

当我尝试编译并运行它时,我得到以下输出:

 debugmain.o:-1: error: too many sections (62303)

可能是什么问题?

仅使用纯Qt您可以执行以下操作:

QString expression_string("3 + Math.sqrt(5) + Math.pow(3,2) + Math.log(5)");
QScriptEngine expression;
double my_val=expression.evaluate(expression_string).toNumber();

你可以做更多的事情,看看 这里 和 这里

实际上

,在我的机器上(Qt 5.5,带有g ++ 5.3的Ubuntu 16.04(,上面的代码不起作用。

尽管答案很老,但我把我的解决方案放在有人发现它有用的情况下。

QScriptEngine 使用 JavaScript 语法。因此,为了使上面的代码正常工作,我不得不将语法更改为:

QString expression_string("3 + Math.sqrt(5) + Math.pow(3,2) + Math.log(5)");
QScriptEngine expression;
double my_val=expression.evaluate(expression_string).toNumber();

按照注释中的请求,这里是如何使用boost::spirit实现算术解析器。首先,你需要下载 boost 压缩包,不要试图从 GitHub 单独克隆 Spirit,因为它有来自其他提升库的依赖关系。

Boost 是巨大的,所以如果你只想要一个足够用于解析器的子集,你可以使用 bcp 提取它。从提升源目录:

cd tools/build/src/engine
./build.sh
cd ../../../bcp
../build/src/engine/b2
cd ../..
dist/bin/bcp fusion/include hana/functional spirit/home/x3 /some/path

bcp将复制所有依赖项。你可以只保留/some/path/boost目录,因为我们需要的所有库都只是标头。

最后,这里是解析器的完整代码。

#include <iostream>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/hana/functional/fix.hpp>
#include <boost/hana/functional/overload.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
using namespace boost::spirit;
namespace hana = boost::hana;
// Define AST. The root is `ast::expr`, which is the first left-hand side
// operand and a list of all operations on the right-hand side. Each operand is
// a recursive `variant` that has `ast::expr` inside.
namespace ast
{
    struct nil {};
    struct signed_;
    struct expr;
    struct operand : x3::variant<
        nil
      , double
      , x3::forward_ast<signed_>
      , x3::forward_ast<expr>
      >
    {
        using base_type::base_type;
        using base_type::operator=;
    };
    struct signed_
    {
        char    sign;
        operand operand_;
    };
    struct operation
    {
        char    operator_;
        operand operand_;
    };
    struct expr
    {
        operand                first;
        std::vector<operation> rest;
    };
} // namespace ast
// Give the grammar access to the fields of AST.
BOOST_FUSION_ADAPT_STRUCT(ast::signed_, sign, operand_)
BOOST_FUSION_ADAPT_STRUCT(ast::operation, operator_, operand_)
BOOST_FUSION_ADAPT_STRUCT(ast::expr, first, rest)
// Arithmetic expression grammar definition.
namespace ArithExpr
{
    x3::rule<class expression, ast::expr   > const expression("expression");
    x3::rule<class term,       ast::expr   > const term("term");
    x3::rule<class factor,     ast::operand> const factor("factor");
    auto const expression_def =
        term
     >> *(
             (x3::char_('+') >> term)
           | (x3::char_('-') >> term)
         );
    auto const term_def =
        factor
     >> *(
             (x3::char_('*') >> factor)
           | (x3::char_('/') >> factor)
         );
    auto const factor_def =
        x3::double_
     | '(' >> expression >> ')'
     | (x3::char_('-') >> factor)
     | (x3::char_('+') >> factor);
    BOOST_SPIRIT_DEFINE(expression, term, factor);
    auto calc = expression;
} // namespace ArithExpr
template <typename Iterator>
double CalcArithExpr(Iterator const &first, Iterator last) {
    ast::expr expr;
    // Build AST.
    if (!x3::phrase_parse(first, last, ArithExpr::calc, x3::ascii::space, expr)) {
        throw std::runtime_error("Cannot parse arithmetic expression");
    }
    // Parse the AST and calculate the result.
    // hana::fix allows recursive lambda call
    auto astEval = hana::fix([](auto self, auto expr) -> double {
        // hana::overload calls a lambda corresponding to the type in the variant
        return hana::overload(
            [](ast::nil) -> double {
                BOOST_ASSERT(0);
                return 0;
            },
            [](double x) -> double { return x; },
            [&](ast::signed_ const &x) -> double {
                double rhs = boost::apply_visitor(self, x.operand_);
                switch (x.sign) {
                    case '-': return -rhs;
                    case '+': return +rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            },
            [&](ast::expr const &x) -> double {
                return std::accumulate(
                    x.rest.begin(), x.rest.end(),
                    // evaluate recursively left-hand side
                    boost::apply_visitor(self, x.first),
                    [&](double lhs, const ast::operation &op) -> double {
                        // evaluate recursively right-hand side
                        double rhs = boost::apply_visitor(self, op.operand_);
                        switch (op.operator_) {
                            case '+': return lhs + rhs;
                            case '-': return lhs - rhs;
                            case '*': return lhs * rhs;
                            case '/': return lhs / rhs;
                        }
                        BOOST_ASSERT(0);
                        return 0;
                    }
                );
            }
        )(expr);
    });
    return astEval(expr);
}
int main(int argc, char *argv[]) {
    auto expr = std::string{"-(4.5 + 5e-1) * 2.22 - 9.1 / 3.45"};
    std::cout << CalcArithExpr(expr.begin(), expr.end()) << std::endl;
}

它计算-(4.5 + 5e-1) * 2.22 - 9.1 / 3.45并输出-13.7377

更新

以下是如何在 Windows 上构建bcp和复制所选标头的说明。虽然,没有任何保证。在 Linux 中一切正常,在 Windows 上它总是跳过一些箍,跳跃的方向总是不可预测的。

话虽如此,请打开PowerShell命令行。那里

Import-Module 'C:Program Files (x86)Microsoft Visual Studio2019ProfessionalCommon7ToolsMicrosoft.VisualStudio.DevShell.dll'
Install-Module VSSetup -Scope CurrentUser
Get-VSSetupInstance

将上面的 2019 替换为您的 VS 版本。对于PowerShell,您只需执行一次操作。剩下的就是每次你需要建造bcp的时候。 上面的Get-VSSetupInstance将打印有关计算机上的Visual Studio实例的信息。写下您想要使用的InstanceId。现在切换到 PowerShell 中的 boost 目录,然后:

Enter-VsDevShell InstanceId -DevCmdArguments '-arch=x64' -SkipAutomaticLocation

其中InstanceId是您从Get-VSSetupInstance获得的 ID。然后从同一个命令提示符

cd toolsbuildsrcengine
& .build.bat
cd ......bcp
..buildsrcengineb2 address-model=64
cd ....
distbinbcp fusioninclude hanafunctional spirithomex3 X:somepathboost