muParser公式库使用简介

muParser是一个跨平台的公式解析库,它可以自定义多参数函数,自定义常量、变量及一元前缀、后缀操作符,二元操作符等,它将公式编译成字节码,所以计算起来非常快。

当前版本V1.28,官方网址http://sourceforge.net/projects/muparser/,这里是关于该库使用交流https://sourceforge.net/forum/forum.php?forum_id=462843

它提供两种方式使用,一种是将它编译进你的程序中,使用C++类,另一种是将它编译成共享库,可以使用其它语言调用,下面主要介绍C接口方式使用该库,本文大部分例子都是muParser库中自带的,也包括本人使用过程中的一点点心得。

一、初始化公式库

#include“muParserDLL.h”

muParserHandle_t hParser;

hParser = mupCreate();//创建公式对象,并返回该对象的句柄

二、设置公式

muChar_t szLine[100];

mupSetExpr(hParser, szLine);//这里szLine一定要是以\0结尾的字符串

三、计算公式

muFloat_t fVal = 0,

fVal = mupEval(hParser);

计算公式中的数值类都是以浮点处理的,其实就是double类型,除了计算浮点数,还可以处理字符串参数

四、释放公式对象

mupRelease(hParser);

五、变量,常量操作

1.定义字符串常量

mupDefineStrConst(hParser, "strBuf", "Hallo welt");

它表示定义一个值为"Hallo welt”的字符串常量strBuf,你可以在公式引用该常量的值,如:a=strBuf,它表示将strBuf的值赋给变量a,当然它要求a为字符串变量

2.定义数值常量

mupDefineConst(hParser, "const1", 1); //表示定义一个名为const1值为1的数值常量

注意常量只能引用它的值,不能改变它的值

3.定义数值变量

muFloat_t var;

mupDefineVar(hParser, "a", &var);

在公式中你可以随意引用或改变变量a的值,注意如果你要全局使用自定的变量a,则不能将变量var定义为局部变量,否则会引起内存地址非法访问

.自定函数

自定义函数有以下几种原型

typedef muFloat_t (*muFun1_t)(muFloat_t);

typedef muFloat_t (*muFun2_t)(muFloat_t, muFloat_t);

typedef muFloat_t (*muFun3_t)(muFloat_t, muFloat_t, muFloat_t);

typedef muFloat_t (*muFun4_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t);

typedef muFloat_t (*muFun5_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);

typedef muFloat_t (*muMultFun_t)(const muFloat_t*, muInt_t);

typedef muFloat_t (*muStrFun1_t)(const muChar_t*);

typedef muFloat_t (*muStrFun2_t)(const muChar_t*, muFloat_t);

typedef muFloat_t (*muStrFun3_t)(const muChar_t*, muFloat_t, muFloat_t);

分为三大类,即固定数值参数的函数(1个或2个或3个或4个或5个数值参数),固定带字符串参数的函数(1个字符串参数,0个或1个或2个数值参数),可变参数函数(一个数值数组参数,一个数组大小参数),下面分别介绍

1.固定数值参数的函数

函数原型:

API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun1_t a_pFun, muBool_t a_bOptimize);

API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun2_t a_pFun, muBool_t a_bOptimize);

API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun3_t a_pFun, muBool_t a_bOptimize);

API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun4_t a_pFun, muBool_t a_bOptimize);

API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, const muChar_t *a_szName, muFun5_t a_pFun, muBool_t a_bOptimize);

//定义一个参数的函数,对参数平方运算

muFloat_t Power(muFloat_t a)

return a*a;

//定义两个参数的函数,求和

muFloat_t Add(muFloat_t a, muFloat b)

{

return a+b;

}

mupDefineFun1(hParser,“power”, Power, 0);//最后一个参数表示是否对它进行优化

  1. mupDefineFun2(hParser, “add”, Add, 0);

公式使用方法:power(4)*add(3,4)

2.固定带字符串参数的函数

函数原型:

API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun1_t a_pFun);

API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun2_t a_pFun);

API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t *a_szName, muStrFun3_t a_pFun);

//定义一个带字符串参数的函数,将字符串表示的十六进制数转换成十进制数

muFloat_t hex(const muChar_t* str)

{

int len = strlen(str);

int ret, tmp, i;

if(len< = 0)

return 0;

for (i = 0; i < len; i++)

{

if(str[i]>= '0'&& str[i]<= '9')

tmp = str[i]-'0';

else if(str[i]>= 'a'&& str[i] <= 'f')

tmp = str[i]-'a'+10;

else if(str[i]>='A'&& str[i]<='F')

tmp = str[i]-'A'+10;

else

return ret;

ret = (ret<<4) | tmp;

}

return ret;

}

mupDefineStrFun1(hParser,“hex”, hex);

公式使用方法:hex(“0FA0”),公式中的字符中参数用双引号括起来

3.可变参数函数

函数原型:

API_EXPORT(void) mupDefineMultFun( muParserHandle_t a_hParser,

const muChar_t* a_szName,

muMultFun_t a_pFun,

muBool_t a_bOptimize);

//求多个参数的和,参数个数不确定

double Sum(const double *a_afArg, int a_iArgc)

{

double fRes=0;

int i=0;

for (i=0; i<a_iArgc; ++i)

fRes += a_afArg[i];

return fRes;

}

mupDefineMultFun(hParser,“sum”, sum, 0);

公式使用:sum(1,2,3,4,5,6,7,8,9)

在公式解析的时候,会自动将所有的参数压入到一个数组中,并将该数组和参数个数传给自定义回调函数

七、定义运算操作符(这可是非常有新意的功能)

运算操作符分为前缀一元操作符,后缀一元操作符,二元操作符等

完全可以实现C中的按位运算(&, | ~ << >>),自加减运算(++--),但不能与库的内置操作符冲突

//按位操作运算(注意按位运算只能运算整型,所以运算前要先转换)

double BitAnd(double a, double b)

{

return (int)a&(int)b;

}

double BitOr(double a, double b)

{

return (int)a|(int)b;

}

double BitXor(double a, double b)

{

return (int)a^(int)b;

}

double BitNot(double a)

{

return ~(int)a;

}

double Shll(double a, double b)

{

return (int)a<<(int)b;

}

double Shlr(double a, double b)

{

return (int)a>>(int)b;

}

mupDefineOprt(hParser, "&", BitAnd, 2, 0);

mupDefineOprt(hParser, "|", BitOr, 2, 0);

mupDefineOprt(hParser, "bxor", BitXor, 2, 0);

mupDefineInfixOprt(hParser, "~", BitNot, 5);

mupDefineOprt(hParser, "<<", Shll, 3, 0);

mupDefineOprt(hParser, ">>", Shlr, 3, 0);

公式使用:((a&b)>>8)bxor((a|b)<<8)

注意该库解析还有一些BUG,所以上面公式并不能正确解析,在自定义的运算符与变量之间添加空格可以暂时解决这个问题,如((a& b)>> 8)bxor((a | b)<<8),在本文后面会介绍如何修正该BUG

八、错误处理

错误处理是很重要的,因为库是用C++写的,对错误采用异常处理方式,所以如果公式解析出错,而又没有捕捉该异常,程序就会挂掉,C接口没有捕捉异常的能力,这里可以使用安装错误处理回调函数解决这个问题,使用方法如下:

void OnError(muParserHandle_t hParser)

{

printf("\nError:\n");

printf("------\n");

printf("Message: %s\n", mupGetErrorMsg(hParser));

printf("Token:   %s\n", mupGetErrorToken(hParser));

}

mupSetErrorHandler(hParser, OnError);

九、显示公式对象的已定义变量,常量,表达式

这些功能使用非常简单,首先取得变量或常量或表达式的个数,然后根据对应的索引位置取出它们的值,我就不多说了,附上相关的API声明

API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser);

API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser);

API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser);

API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);

API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);

API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_pVar);

十、变量工厂

也就是在解析到一个未定义的标识符时,根据用户注册的回调函数规则自动创建以该标识符命名的变量,如下示例:

muFloat_t* AddVariable(const muChar_t* a_szName, void* pUserData)

{

static double afValBuf[PARSER_MAXVARS];//定义变量缓冲区

static int iVal = 0;

printf("Generating new variable \"%s\" (slots left: %d)\n", a_szName, PARSER_MAXVARS-iVal);

afValBuf[iVal] = 0;

if (iVal>=PARSER_MAXVARS-1)

{

printf("Variable buffer overflow.");

return NULL;

}

return& afValBuf[iVal++]; //返回存储该变量值的地址

}

mupSetVarFactory(hParser, AddVariable, NULL);

十一、关于BUG

1.解析操作符的问题,如果你定义了“&”操作符, 那么公式 a&b不会工作,它首先解析出变量a,然后解析”&b”,发现”&b”即不是操作符,也不是常量,变量,或函数,最后就抛出异常,问题出在muParserTokenReader.cpp文件中的ExtractToken函数的算法上,

int ParserTokenReader::ExtractToken( const char_type *a_szCharSet,

string_type &a_sTok, int a_iPos ) const

{

int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos);

if (iEnd==(int)string_type::npos)

iEnd = (int)m_strFormula.length();

a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd);

a_iPos = iEnd;

return iEnd;

}

它采用find_first_not_of来搜索给定字符串与指定集合不匹配的第一个元素位置,因为操作符命名可以包含算术运算符,英文字母 ,所以会认为&b也是一个合法的操作符名,但要修正不能修改该函数,因为变量,函数等解析都会调用该函数,我们要修正的只是操作符解析算法,即IsOprt函数

以下是我的解决方法:

bool ParserTokenReader::IsOprt(token_type &a_Tok)

{

string_type strTok;

int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), m_iPos);

if(iEnd == string_type::npos)

iEnd = m_strFormula.length();

while(iEnd>m_iPos)

{

strTok = string_type(m_strFormula.begin()+m_iPos, m_strFormula.begin()+iEnd);

funmap_type::const_iterator item = m_pOprtDef->find(strTok);

if(item!=m_pOprtDef->end())

break;

iEnd--;

}

//int iEnd = ExtractToken(m_pParser->ValidOprtChars(), strTok, m_iPos);

if (iEnd==m_iPos)

return false;

funmap_type::const_iterator item = m_pOprtDef->find(strTok);

if (item==m_pOprtDef->end())

return false;

a_Tok.Set(item->second, strTok);

if (m_iSynFlags & noOPT)

{

// An operator was found but is not expected to occur at

// this position of the formula, maybe it is an infix

// operator, not a binary operator. Both operator types

// can share characters in their identifiers.

if ( IsInfixOpTok(a_Tok) ) return true;

// nope, no infix operator

Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());

}

m_iPos = (int)iEnd;

m_iSynFlags = noBC | noOPT | noCOMMA | noPOSTOP | noEND | noBC | noASSIGN;

return true;

}

思路是首先读出最长合法操作符名,然后判断是否被定义了,如果没有则再读取次长操作符名判断是否被定义,依次类推,直到字符串为空,如果找到操作符名被定义了,则立即返回,当然此法也可能引起其它的问题。

2.也是关于操作符解析问题,如果你定义了一个名为or的二元操作符,同时还定义了一个名为order的函数,那么解析时会将order解析为or加上未定义的der,不过这个问题在下一个版本中已经修正了。解决方法如下:

将函数ReadNextToken中的如下语句交换即可

if ( IsOprt(tok) )   return SaveBeforeReturn(tok);

if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok);

//To:

if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok);

if ( IsOprt(tok) )   return SaveBeforeReturn(tok);

3.在错误处理时应该注意,不能同时调用以下函数,否则程序一定挂掉

API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser);

API_EXPORT(muInt_t) mupGetErrorCode(muParserHandle_t a_hParser);

API_EXPORT(muInt_t) mupGetErrorPos(muParserHandle_t a_hParser);

API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser);

API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser);

原因很简单,当解析出错时,一定有错误消息和错误标识符,但不一定有错误码、出错位置、出错表达式(表达式可能为空),这应该是对应C接口上的设计缺陷。

文件: muParser使用简介.pdf
大小: 167KB
下载: 下载

附上本文的PDF文档

By Hyrish

2008-04-05

muParser公式库使用简介相关推荐

  1. muParser公式库使用简介( 转)

    muParser是一个跨平台的公式解析库,它可以自定义多参数函数,自定义常量.变量及一元前缀.后缀操作符,二元操作符等,它将公式编译成字节码,所以计算起来非常快. 当前版本V1.28,官方网址http ...

  2. Raphael Js矢量库API简介:

    Raphael Js矢量库API简介: Raphael Javascript 是一个 Javascript的矢量库. 2010年6月15日,著名的JavaScript库ExtJS与触摸屏代码库项目jQ ...

  3. db2top详细使用方法_Py之PIL:Python的PIL库的简介、安装、使用方法详细攻略

    Py之PIL:Python的PIL库的简介.安装.使用方法详细攻略 目录 PIL库的简介 PIL库的安装 PIL库的用方法 1.几何图形的绘制与文字的绘制 2.绘制图形的各种案例 PIL库的简介 PI ...

  4. python compiler库_Python之compiler:compiler库的简介、安装、使用方法之详细攻略

    Python之compiler:compiler库的简介.安装.使用方法之详细攻略 目录 compiler库的简介 compiler库的安装 compiler库的使用方法 compiler库的简介 根 ...

  5. 【Android NDK 开发】Android Studio 使用 CMake 导入静态库 ( CMake 简介 | 构建脚本路径配置 | 引入静态库 | 指定静态库路径 | 链接动态库 )

    文章目录 I . CMake 简介 II . Android Studio 中 CMake 引入静态库流程 III . 指定 CMake 最小版本号 IV . 导入函数库 ( 静态库 / 动态库 ) ...

  6. Python之ffmpeg-python:ffmpeg-python库的简介、安装、使用方法之详细攻略

    Python之ffmpeg-python:ffmpeg-python库的简介.安装.使用方法之详细攻略 目录 ffmpeg-python库的简介 ffmpeg-python库的安装 ffmpeg-py ...

  7. Python之fastai:fastai库的简介、安装、使用方法之详细攻略

    Python之fastai:fastai库的简介.安装.使用方法之详细攻略 目录 fastai库的简介 fastai库的安装 fastai库的使用方法 1.计算机视觉分类

  8. Python之tushare:tushare库的简介、安装、使用方法之详细攻略

    Python之tushare:tushare库的简介.安装.使用方法之详细攻略 目录 tushare库的简介 tushare库的安装 tushare库的使用方法 1.基础用法 tushare库的简介 ...

  9. Py之tornado:tornado库的简介、安装、使用方法之详细攻略

    Py之tornado:tornado库的简介.安装.使用方法之详细攻略 目录 tornado库的简介 tornado库的安装 tornado库的使用方法 1.简单的Tornado的"Hell ...

最新文章

  1. python威氏符号秩次检验(Wilcoxon Signed-Rank Test)
  2. pg_dump 详解/使用举例
  3. 红帽虚拟化RHEV3.2创建虚拟机(图文Step by Step)
  4. python批量解压文件_python 批量解压压缩文件的实例代码
  5. Initializer provides no value for this binding element and the binding element has no default value
  6. 【渝粤教育】国家开放大学2018年春季 0674-22T财务管理 参考试题
  7. PyCharm安装及配置
  8. tensorflow版本升级后的各种方法问题
  9. Spring Boot+JPA 查询数据方式与代码演示
  10. Qt-做一个快速打包插件(一键完成项目软件打包)
  11. 【LAMMPS系列】LAMMPS安装WIN并行版
  12. TranslateAnimation类:位置变化动画类 (类似tab切换效果)
  13. html的函数都有什么,yearfrac函数什么意思
  14. 证券公司信息化7-资产管理业务的沿革。为什么要有资产管理系统?
  15. SpringBoot - 自动装配 源码解析
  16. python练习54:取一个整数a从右端开始的4〜7位
  17. 2014华中首届手游创意大赛
  18. 解决 zbrush导出的模型在blender里没有颜色的问题
  19. 安全态势感知系统java_代码分析平台CodeQL学习手记(十三) - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com...
  20. Flink on Yarn的两种模式及HA

热门文章

  1. 《C语言入门100例》第二例 数列求和
  2. 计算机英语的词汇量,英语词汇量多少才够用
  3. 简单几步搞定Mac电脑快速返回桌面的操作!
  4. Windows11无法打开Windows安全中心(需要使用新应用打开此windowsdefender链接)
  5. 电脑白屏,电脑白屏了按哪个键可以修复?
  6. 加州大学欧文分校 计算机工程,美国加州大学欧文分校计算机工程本科.pdf
  7. 《痞子衡嵌入式半月刊》 第 48 期
  8. 排列组合相关笔试面试题(C++)
  9. 千兆以太网(四)——UDP协议
  10. 基于BALKANFamilyTreeJS插件的家谱可视化项目功能Demo