Bootstrap

发布一个C++编写的自定义公式解析器

由于项目需要,15年前自己用c++编写了一个简易的公式编辑器,支持+,-,*,/,函数,自定义变量等,非常实用,现在分享给大家.

calculateformula.h

#ifndef CALCULATEFORMULA_H
#define CALCULATEFORMULA_H

#include <functional>
#include <qstring.h>
#include <qstack.h>
#include <QVariant>
class CalculateFormula
{
public:
    constexpr static double MATH_PI = 3.14159265358979323846;
    using CallbackGetSymbolValue = std::function<double(const QString&, QVariant& value, bool& bOK)>;
    CalculateFormula(CallbackGetSymbolValue symbolHandler, QVariant value);

    bool Calculate(const QString& strFormula,double& dResult);
    QString GetPhrase(const QString& strPhrase,int& newIndex,int& type);
    double CalculateValue(double v1,double v2,int type,bool& bOk);
    double ToDouble(const QString& variable, bool& bOk);
    bool IsHighPriority(int type1,int type2);
    bool ProcessStackData(int type);
    QString * GetParam(const QString& Formula,  int& index,int countParam);
    double DoFunction(const QString& funName, const QString& Formula, int& index, bool& bOk);
    double CalculateSymbol(const QString& symbol, bool& bOk);
    static int GetCharType(const char c);

    double Sqrt(QString param [],bool& bOk);
    double Pow2(QString param [],bool& bOk);
    double Exp(QString param [],bool& bOk);
    double Abs(QString param [],bool& bOk);
    double Pow(QString param [],bool& bOk);
    double IsNull(QString param [],bool& bOk);
    double Log(QString param [],bool& bOk);
    double Sin(QString param [],bool& bOk);
    double Cos(QString param [],bool& bOk);
    double Tan(QString param [],bool& bOk);
    double Atan(QString param [],bool& bOk);
    double Atanr(QString param [],bool& bOk);
public:
    CallbackGetSymbolValue OnGetSymbolValue;
private:
    QStack<double> m_stkData;
    QStack<int> m_stkOperator;
    QString m_strErrorMessage="";
    QVariant m_objValue;
    short arrPriority[7][7]
        {
            //+,-,*,/,(,),=,
            /*+*/  { 0, 0,1,1,2,3,3},
            /*-*/  { 0, 0,1,1,2,3,3},
            /* * */{-1,-1,0,0,1,2,2},
            /* / */{-1,-1,0,0,1,2,2},
            /* ( */{-2,-2,-1,-1,0,1,1},
            /*  )*/{-3,-3,-2,-2,-1,0,0},
            /* =*/ {-3,-3,-2,-2,-1,0,0}
        };
};

#endif // CALCULATEFORMULA_H

calculateformula.cpp

#include "calculateformula.h"

CalculateFormula::CalculateFormula(CallbackGetSymbolValue symbolHandler, QVariant value)
{
    OnGetSymbolValue=symbolHandler;
    m_objValue=value;
}

double CalculateFormula::ToDouble(const QString& variable, bool& bOk)
{
    return variable.toDouble(&bOk);
}

/// <summary>
/// 判断type1的运算优先级是否大于type2的运算优先级
/// </summary>
/// <param name="type1"></param>
/// <param name="type2"></param>
/// <returns></returns>
bool CalculateFormula::IsHighPriority(int type1,int type2)
{
    if (arrPriority[type1][type2]<=0)
    {
        return true;
    }
    return false;
}

bool CalculateFormula::ProcessStackData(int type)
{
    bool bOk=false;
    if (m_stkOperator.count() == 0)
    {
        m_strErrorMessage = "表达式格式不正确";
        return false;
    }
    if (m_stkData.count() == 0)
    {
        m_strErrorMessage = "表达式格式不正确";
        return false;
    }

    int realType=m_stkOperator.pop();
    double v2=m_stkData.pop();
    while(true)
    {
        if (realType==4&&type==5)
        {
            break;
        }
        if (m_stkData.count()==0)
        {
            m_strErrorMessage="表达式格式不正确";
            return false;
        }
        v2=CalculateValue(m_stkData.pop(),v2,realType,bOk);
        if (!bOk)
        {
            return false;
        }
        if (m_stkOperator.count()>0)
        {
            if (type!=5)
            {
                if (IsHighPriority(m_stkOperator.top(),realType))
                {
                    realType=m_stkOperator.pop();
                    if (realType==4)
                    {
                        m_stkOperator.push(realType);
                        break;
                    }
                }
                else
                {
                    break;
                }
            }
            else
            {
                realType=m_stkOperator.pop();
            }
        }
        else
        {
            break;
        }
    }
    m_stkData.push(v2);
    if (type!=4&&type!=5)
    {
        m_stkOperator.push(type);
    }
    return true;
}

double CalculateFormula::Sqrt(QString param [],bool& bOk)
{
    bOk=true;
    double ret=0.0;
    CalculateFormula cf(OnGetSymbolValue,m_objValue);
    if (cf.Calculate(param[0], ret))
    {
        return sqrt(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Sqrt有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Pow2(QString param [],bool& bOk)
{
    bOk=true;
    double ret=0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0], ret))
    {
        return ret*ret;
    }
    bOk = false;
    m_strErrorMessage = "函数POW2有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Sin(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return sin(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Sin有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Cos(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return cos(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Cos有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Tan(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return tan(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Tan有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Exp(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return exp(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Exp有错误" + param[0];
                        return 0.0;
}
double CalculateFormula::Atan(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return atan(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Atan[返回角度值]有错误" + param[0];
    return 0.0;
}
double CalculateFormula::Atanr(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return 360 / atan(ret) * MATH_PI;
    }
    bOk = false;
    m_strErrorMessage = "函数Atanr[反回弧度值]有错误" + param[0];
    return 0.0;
}
double CalculateFormula::Log(QString param [],bool& bOk)
{
    bOk = true;
    double ret1 = 0.0;
    double ret2 = 0.0;
    CalculateFormula cf_1(OnGetSymbolValue, m_objValue);
    CalculateFormula cf_2(OnGetSymbolValue, m_objValue);

    if (cf_1.Calculate(param[0],  ret1) && ret1 !=0 &&
        cf_2.Calculate(param[1],  ret2))
    {
        return log(ret1)/log(ret2);
    }
    bOk = false;
    m_strErrorMessage = "函数Log有错误:Log(" + param[0] + "," + param[1] + ")";
                        return 0.0;
}
double CalculateFormula::Pow(QString param [],bool& bOk)
{
    bOk = true;
    double ret1 = 0.0;
    double ret2 = 0.0;
    CalculateFormula cf_1(OnGetSymbolValue, m_objValue);
    CalculateFormula cf_2(OnGetSymbolValue, m_objValue);

    if (cf_1.Calculate(param[0],  ret1) &&
        cf_2.Calculate(param[1],  ret2))
    {
        return pow(ret1, ret2);
    }
    bOk = false;
    m_strErrorMessage = "函数Log有错误:Pow(" + param[0] + "," + param[1] + ")";
                        return 0.0;
}
double CalculateFormula::Abs(QString param [],bool& bOk)
{
    bOk = true;
    double ret = 0.0;
    CalculateFormula cf(OnGetSymbolValue, m_objValue);
    if (cf.Calculate(param[0],  ret))
    {
        return fabs(ret);
    }
    bOk = false;
    m_strErrorMessage = "函数Abs有错误" + param[0];
                        return 0.0;
}

double CalculateFormula::IsNull(QString param [],bool& bOk)
{
    bOk = true;
    double ret1 = 0.0;
    CalculateFormula cf_1(OnGetSymbolValue, m_objValue);
    CalculateFormula cf_2(OnGetSymbolValue, m_objValue);

    if (cf_1.Calculate(param[0],  ret1)) return ret1;
    if (cf_2.Calculate(param[1],  ret1)) return ret1;

    bOk = false;
    m_strErrorMessage = "函数IsNull有错误:IsNull(" + param[0] + "," + param[1] + ")";
                        return 0.0;
}

QString * CalculateFormula::GetParam(const QString& Formula,  int& index,int countParam)
{
                        int indexParam=0;
                        int countBrace=1;
                        QString * arrParam=new QString[countParam];
                        for(int i=0;i<countParam;i++){
        arrParam[i]=nullptr;
                        }
                        if (Formula[index]!='(')
                        {
        m_strErrorMessage = "表达式格式错误[缺少左括号]";
        return nullptr;
                        }
                        index++;
                        while (index<Formula.size())
                        {
        if (Formula[index]=='(')
        {
            countBrace++;
        }
        else if (Formula[index]==')')
        {
            countBrace--;
        }
        if (countBrace==0)
        {
            index++;
            break;
        }
        if (Formula[index]==',' && countBrace == 1)
        {
            indexParam++;
            index++;
            continue;
        }
        if (indexParam < countParam)
        {
            if (Formula[index] != ' ')
            {
                arrParam[indexParam] += Formula[index];
            }
        }
        index++;
                        }
                        if (indexParam<countParam && arrParam[countParam-1] != nullptr)
                        {
        return arrParam;
                        }
                        m_strErrorMessage = "表达式参数错误,应为"+QString::number(countParam)+"个参数";
                                            return nullptr;
}
double CalculateFormula::DoFunction(const QString& funName, const QString& Formula, int& index, bool& bOk)
{
    double val=0.0;
    bOk=true;
    QString fun=funName.toLower().trimmed();

    if (fun== "exp"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Exp(strParam,  bOk);
        }
        delete[] strParam;

    } else         if (fun== "abs"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Abs(strParam,  bOk);
        }
        delete[] strParam;

    }     else    if (fun== "pow"){
        QString* strParam = GetParam(Formula,  index, 2);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Pow(strParam,  bOk);
        }
        delete[] strParam;


    }   else      if (fun== "isnull"){
        QString* strParam = GetParam(Formula,  index, 2);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = IsNull(strParam,  bOk);
        }
        delete[] strParam;


    }    else     if (fun== "log"){
        QString* strParam = GetParam(Formula,  index, 2);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Log(strParam,  bOk);
        }
        delete[] strParam;


    }    else     if (fun== "sin"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Sin(strParam,  bOk);
        }
        delete[] strParam;


    }     else    if (fun== "cos"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Cos(strParam,  bOk);
        }
        delete[] strParam;


    }    else     if (fun== "tan"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Tan(strParam,  bOk);
        }
        delete[] strParam;


    }   else      if (fun== "atanr"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Atanr(strParam,  bOk);
        }
        delete[] strParam;


    }    else     if (fun== "sqrt"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Sqrt(strParam,  bOk);
        }
        delete[] strParam;


    }    else     if (fun== "pow2"){
        QString* strParam = GetParam(Formula,  index, 1);
        if (strParam==nullptr)
        {
            bOk = false;
        } else {
            val = Pow2(strParam,  bOk);
        }
        delete[] strParam;


    } else {
        m_strErrorMessage="不支持的函数"+funName;
        bOk=false;
    }
    return val;
}
double CalculateFormula::CalculateSymbol(const QString& symbol, bool& bOk)
{
    bOk=false;
    if (OnGetSymbolValue!=nullptr)
    {
        double ret=OnGetSymbolValue(symbol,m_objValue, bOk);
        if (!bOk)
        {
            m_strErrorMessage="没有定义的变量"+symbol;
            return 1.0;
        }
        return ret;
    }
    m_strErrorMessage="本程序不能计算变量"+symbol;
    return 1.0;
}
bool CalculateFormula::Calculate(const QString& strFormula,double& dResult){
    int    index=0;
    int    type=-1;
    bool   bOk=false;
    double dTemp=0.0;
    QString strPhrase=GetPhrase(strFormula,index,type);
    dResult=0.0;
    m_stkData.clear();
    m_stkOperator.clear();
    while (strPhrase!="")
    {
        m_strErrorMessage+=strPhrase;
        switch(type)
        {
        case 9:
            dTemp=DoFunction(strPhrase,strFormula,index, bOk);
            if (!bOk)
            {
                return false;
            }
            m_stkData.push(dTemp);
            break;
        case 7:
            dTemp=ToDouble(strPhrase, bOk);
            if (!bOk)
            {
                m_strErrorMessage="表达式输入有误"+strPhrase;
                return false;
            }
            m_stkData.push(dTemp);
            break;
        case 8:
            dTemp=CalculateSymbol(strPhrase, bOk);
            if (!bOk)
            {

                return false;
            }
            m_stkData.push(dTemp);
            break;
        default:
            if (type>=0&&type<=6)
            {
                if (m_stkOperator.empty())
                {
                    m_stkOperator.push(type);
                }
                else
                {
                    if ((type==4||m_stkOperator.top()==4||!IsHighPriority(m_stkOperator.top(),type)) && type!=5)
                    {
                        m_stkOperator.push(type);
                    }
                    else
                    {
                        if (!ProcessStackData(type))
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                m_strErrorMessage="不支持的运算符"+strPhrase;
                return false;
            }
            break;
        }
        strPhrase=GetPhrase(strFormula,index,type);
    }
    if (m_stkData.count()>0)
    {
        dResult=m_stkData.pop();
    }
    index=0;
    while(!m_stkOperator.empty())
    {
        if (m_stkData.empty())
        {
            m_strErrorMessage="表达式不正确";
            return false;
        }
        int tType=m_stkOperator.pop();
        if (tType==5)
        {
            index++;
        }
        else if (tType==4)
        {
            index--;
        }
        dResult=CalculateValue(m_stkData.pop(),dResult,tType,bOk);
        if (!bOk)
        {
            return false;
        }
    }
    return true;
}

double CalculateFormula::CalculateValue(double v1,double v2,int type,bool& bOk)
{
    bOk=true;
    switch(type)
    {
    case 0:
        return v1+v2;
    case 1:
        return v1-v2;
    case 2:
        return v1*v2;
    case 3:
        if (v2==0)
        {
            m_strErrorMessage=QObject::tr("除数为零");
            bOk=false;
            return 0;
        }
        return v1/v2;
    }
    m_strErrorMessage=QObject::tr("未知错误:%1").arg(type);
    bOk=false;
    return 0.0;
}

int CalculateFormula::GetCharType(const char c)
{
    switch(c)
    {
    case '+':
        return 0;
    case '-':
        return 1;
    case '*':
        return 2;
    case '/':
        return 3;
    case '(':
        return 4;
    case ')':
        return 5;
    case '=':
        return 6;
    default:
        if ((c>='0'&&c<='9')||c=='.')
        {
            return 7;// is number
        }
        break;
    }
    return 8;//is char
}

QString CalculateFormula::GetPhrase(const QString& strPhrase,int& newIndex,int& type)
{
    QString strTemp="";
    int i=newIndex;
    int ret=-1;
    type=-1;
    bool bSecondDispare=false;
    if (strPhrase.isEmpty() || strPhrase.size()<=newIndex)
    {
        return "";
    }
    ret=GetCharType(strPhrase[i].toLatin1());
    if (ret==1||ret==0)
    {
        if (i==0)
        {
            do
            {
                strTemp+=strPhrase[i];
                i++;
                if (i>=strPhrase.size())
                {
                    break;
                }
                if ((strPhrase[i]=='E'||strPhrase[i]=='e')&&!bSecondDispare)
                {
                    bSecondDispare=true;
                    strTemp+='E';
                    i++;
                    if (i>=strPhrase.size())
                    {
                        break;
                    }
                    if (strPhrase[i]=='+'||strPhrase[i]=='-')
                    {
                        strTemp+=strPhrase[i];
                        i++;
                        if (i>=strPhrase.size())
                        {
                            break;
                        }
                    }

                }

            }while(GetCharType(strPhrase[i].toLatin1())==7);
            newIndex=i;
            type=7;
            return strTemp;
        }
        else if (GetCharType(strPhrase[i-1].toLatin1())>=0&&GetCharType(strPhrase[i-1].toLatin1())<=4)
        {
            do
            {
                strTemp+=strPhrase[i];
                i++;
                if (i>=strPhrase.size())
                {
                    break;
                }
                if ((strPhrase[i]=='E'||strPhrase[i]=='e')&&!bSecondDispare)
                {
                    bSecondDispare=true;
                    strTemp+='E';
                    i++;
                    if (i>=strPhrase.size())
                    {
                        break;
                    }
                    if (strPhrase[i]=='+'||strPhrase[i]=='-')
                    {
                        strTemp+=strPhrase[i];
                        i++;
                        if (i>=strPhrase.size())
                        {
                            break;
                        }
                    }

                }

            }while(GetCharType(strPhrase[i].toLatin1())==7);
            newIndex=i;
            type=7;
            return strTemp;
        }
        else
        {
            type=ret;
            newIndex=i+1;
            return strPhrase[i];
        }
    }
    else if (ret>=0&&ret<=6)
    {
        type=ret;
        newIndex=i+1;
        return strPhrase[i];
    }
    else if (ret==7)
    {
        do
        {
            strTemp+=strPhrase[i];
            i++;
            if (i>=strPhrase.size())
            {
                break;
            }
            if ((strPhrase[i]=='E'||strPhrase[i]=='e')&&!bSecondDispare)
            {
                bSecondDispare=true;
                strTemp+='E';
                i++;
                if (i>=strPhrase.size())
                {
                    break;
                }
                if (strPhrase[i]=='+'||strPhrase[i]=='-')
                {
                    strTemp+=strPhrase[i];
                    i++;
                    if (i>=strPhrase.size())
                    {
                        break;
                    }
                }

            }
        }while(GetCharType(strPhrase[i].toLatin1())==7);
        newIndex=i;
        type=ret;
        return strTemp;
    }
    else if (ret==8)
    {
        do
        {
            strTemp+=strPhrase[i];
            i++;
            if (i>=strPhrase.size())
            {
                break;
            }
        }while(GetCharType(strPhrase[i].toLatin1())>=7);
        newIndex=i;
        if (i<strPhrase.size()&&strPhrase[i]=='(')
        {
            type=9;
        }
        else
        {
            type=ret;
        }
        return strTemp;
    }
    return "";
}

使用方法


double s(const QString& symbol, QVariant& v, bool& bOK){
    if (symbol=="a"){
        bOK=true;
        return 1;
    }
    if (symbol=="b"){
        bOK=true;
        return 2;
    }
    bOK=false;
    return 0;
}

QVariant vv;//对应变量值获取函数中的参数v
    double dRet;
    CalculateFormula f(s,vv);
    f.Calculate("a+b", dRet);
    QMessageBox::information(nullptr,"",QString::number(dRet));

;