前言
最近做嵌入式设备开发刚好需要用到对数据的公式处理,写了一个四则运算器,顺便分享给有需要各位
提示:以下为正文内容
目标功能
假设有如下参数:
float value = 5 ;
char* formula = " 1 * 2 / 3 + 4 / x + 6 " ;
实现将设备采集的数值替换公式中的字符 ’ x ’ ,并计算出结果。这里封装了两个工具函数来实现此需求,第一个公式用于将数值 value 代入公式中的字符 ’ x ',第二个公式用于计算四则运算式。
/*
* 功能: 字符串的指定内容替换
* 参数:
* str 目标字符串
* oldstr 被替换的字符串
* newstr 替换的字符串
*/
char *Utils_Strrpc(char *str, char *oldstr, char *newstr) ;
/*
* 功能: 四则运算
* 参数:
* formula 运算公式
* length 公式长度
*/
CALCULATERESULT utils_Calculate(char* formula, int length) ;
四则运算函数返回的结构体如下:
/*
* 内容: 四则运算结果
* 目标函数:
* utils_Calculate
*/
typedef struct CALCULATERESULTSTRUCT {
int err; // 格式错误码
float value; // 运算值
} CALCULATERESULT;
不带括号的公式处理
我们先以不带括号的公式为例,有公式如下:
char* formula = " 1 * 2 / 3 + 4 / 5 + 6 " ;
第一步,我们建立两个数组,分别存储数字和运算符:
digits = { 1 2 3 4 5 6 }
operators = { * / - / + }
四则运算第一个要处理的问题就是 “加减” 与 ”乘除“ 运算符的执行先后问题,我们来看下面两组式子:
_1 = " 1 + 2 * 3 " ;
// _2 = " 1 + 0 ? 6 " ;
_2 = " 1 + 0 + 6 " ;
_3 = " 1 - 6 / 2 " ;
// _4 = " 1 - 0 ? 3 " ;
_4 = " 1 - 0 - 3 " ;
不难看出1和2、3和4两组式子的结果是相同的。所以在这里,我们解决问题的方法是:
将 ”乘除“ 运算符的左值变为0,将右值变为左右两值"乘除"运算的结果;
将 "乘除" 运算符变为左边第一个"加减"运算符,如为公式的第一个运算符,则为默认为'+';
紧接着示例公式,我们处理如下:
digits { 1 2 3 4 5 6 }
operators { * / - / + }
--------------------------------------------------------------
digits { 0 2 3 4 5 6 }
operators { ? / - / + }
--------------------------------------------------------------
digits { 0 0 6 4 5 6 }
operators { ? ? - / + }
--------------------------------------------------------------
digits { 0 0 6 0 20 6 }
operators { ? ? - ? + }
--------------------------------------------------------------
digits { 0 0 6 0 20 6 }
operators { ? ? - ? + }
--------------------------------------------------------------
digits { 0 0 6 0 20 6 }
operators { + + - - + }
第二步,我们定义一个结果值 value,让结果值等于digits[ 0 ],并根据符号依次加减运算digits[ 1 ] 到 digits [ n ],最后value的值就是我们公式处理的结果值,紧接着示例公式,我们处理如下:
value = digits[0]
0 0
-----------------------------------------------------
value operators[0] digits[1]
0 + 0 = 0
-----------------------------------------------------
value operators[1] digits[2]
0 + 6 = 6
-----------------------------------------------------
value operators[2] digits[3]
6 - 0 = 6
-----------------------------------------------------
value operators[3] digits[4]
6 - 20 = -20
-----------------------------------------------------
value operators[4] digits[5]
-20 + 6 = -16
带括号的公式处理
四则运算第二个需要处理的问题就是括号带来的运算优先级问题,这里我们使用递归的方式处理,有公式如下:
char *formula = " 1 * ( 2 + ( 3 + 4 ) / 5 + 6 ) " ;
因为有两层括号,实际程序执行时会执行两次递归。我们建立两个数组,分别存储数字和运算符,函数执行递归情况如下:
----------------------------------------------------- // 开始层函数的数组
digits { 1 ? }
operators { * }
----------------------------------------------------- // 第一层递归函数的数组
digits { 2 ? 5 6}
operators { + / + }
----------------------------------------------------- // 第二层递归函数的数组
digits { 3 4}
operators { + }
-----------------------------------------------------
第二层递归函数按照 “不带括号的公式处理” 执行完后,返回的计算结果 ’ 7 ’ 替代第一层递归函数素组digits中 ’ ? ’ 位的值,得到如下:
----------------------------------------------------- // 第一层递归函数的数组
digits { 1 ? }
operators { * }
----------------------------------------------------- // 第二层递归函数的数组
digits { 2 7 5 6}
operators { + / + }
同理,执行第三层函数,得到如下:
----------------------------------------------------- // 第一层递归函数的数组
digits { 1 9.4 }
operators { * }
最后即可得出结果为10.4。
源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CALCULATE_ERR_REPEAT -1 // 重复运算符
#define CALCULATE_ERR_SYMBOL -2 // 非法字符
#define CALCULATE_ERR_NUMBER -3 // 数字与运算符数量不对应
#define CALCULATE_ERR_BEYOND -4 // 数字数量,运算符数量,数字长度超出预设值
#define CALCULATE_MAX_DIGITS 30 // 限制数字最大数量
#define CALCULATE_MAX_OPERATOR 30 // 限制运算符最大数量
#define CALCULATE_MAX_DIGIT 30 // 限制数字最大位数
/*
* 内容: 运算结果
* 函数: utils_Calculate
*/
typedef struct CALCULATERESULTSTRUCT {
int err;
float value;
} CALCULATERESULT;
/*
* 功能: 字符串指定内容替换
* 参数:
* str 目标字符串
* oldstr 被替换的字符串
* newstr 替换的字符串
*/
char *Utils_Strrpc(char *str, char *oldstr, char *newstr)
{
char bstr[strlen(str)];
memset(bstr, 0, sizeof(bstr));
int i;
for (i = 0; i < strlen(str); i++) {
if (!strncmp(str + i, oldstr, strlen(oldstr))) {
strcat(bstr, newstr);
i += strlen(oldstr) - 1;
} else {
strncat(bstr, str + i, 1);
}
}
strcpy(str, bstr);
return str;
}
/*
* 功能: 实时数据四则运算处理
* 参数:
* formula 运算公式
* length 公式长度
*/
CALCULATERESULT Utils_Calculate(char* formula, int length)
{
CALCULATERESULT result = {0, 0.0f}; // 返回结构体
int idx; // 索引
int digitsNum = 0; // 数字数量
float digits[CALCULATE_MAX_DIGITS]; // 存储数据的数组
memset(digits, '\0', sizeof(digits));
int optNum = 0; // 运算符数量
char operator[CALCULATE_MAX_OPERATOR]; // 存储运算符的数组
memset(operator, '\0', sizeof(operator));
int digitNum = 0; // 单个数字字符串
char digit[CALCULATE_MAX_DIGIT]; // 存储单个数字字符串的数组
memset(digit, '\0', sizeof(digit));
/* 提取数字和符号到数组 */
char *pointer = formula;
while (length--)
{
switch(*pointer) {
case '+':
case '-':
case '*':
case '/':
if (0 == digitNum && '-' == *pointer) {
digit[digitNum++] = *pointer;
} else {
if (-1 == digitNum) {
digitNum = 0;
goto NEXT;
}
if (0 == digitNum) {
result.err = CALCULATE_ERR_REPEAT;
goto END;
}
if (CALCULATE_MAX_DIGITS == digitsNum - 1) {
result.err = -4;
goto END;
}
digits[digitsNum++] = atof(digit);
memset(digit, '\0', sizeof(digit));
digitNum = 0;
NEXT:
operator[optNum++] = *pointer;
}
break;
case '(':
{
char *pointer_son;
int ExistEnd = 0;
pointer_son = ++pointer;
while(length--) {
if ('(' == *pointer) {
ExistEnd--;
}
if (')' == *pointer) {
ExistEnd++;
}
if (1 == ExistEnd) {
break;
}
pointer++;
}
/* 括号内的字符串执行递归 */
CALCULATERESULT result_son = Utils_Calculate(pointer_son, pointer - pointer_son);
if (0 > result_son.err) {
result.err = result_son.err;
goto END;
}
digits[digitsNum++] = result_son.value;
memset(digit, '\0', sizeof(digit));
digitNum = -1;
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
digit[digitNum++] = *pointer;
break;
case ' ':
case '\n':
break;
default:
result.err = CALCULATE_ERR_SYMBOL;
goto END;
}
if (0 == length && 0 < digitNum) {
digits[digitsNum++] = atof(digit);
memset(digit, '\0', sizeof(digit));
digitNum = 0;
}
pointer ++;
}
if (digitsNum != optNum + 1) {
result.err = CALCULATE_ERR_NUMBER;
goto END;
}
/* 消除"乘除"运算符 */
for (idx = 0; idx < optNum; idx ++) {
if ('*' == operator[idx]) {
digits[idx+1] = digits[idx] * digits[idx+1];
digits[idx] = 0;
operator[idx] = '?';
}
if ('/' == operator[idx]) {
digits[idx+1] = digits[idx] / digits[idx+1];
digits[idx] = 0;
operator[idx] = '?';
}
}
for (idx = 0; idx < optNum; idx ++) {
if ('?' == operator[idx]) {
if (0 == idx) {
operator[idx] = '+';
} else {
operator[idx] = operator[idx-1];
}
}
}
/* 顺序执行"加减"运算 */
result.value = digits[0];
for (idx = 0; idx < optNum; idx ++) {
if ('+' == operator[idx]) {
result.value += digits[idx + 1];
}
if ('-' == operator[idx]) {
result.value -= digits[idx + 1];
}
}
END:
return result;
}
int main(int argc, char* argv[])
{
char formula[1024];
if (3 != argc) {
printf("请输入正确的参数\n");
return 0;
}
sprintf(formula, "%s", argv[2]);
Utils_Strrpc(formula, "x", argv[1]);
CALCULATERESULT resault = Utils_Calculate(formula, strlen(formula));
printf("%g\n", resault.value);
return 0;
}
测试
程序第一个参数为数值,第二个参数为公式,执行测试:
root@zth:~/newDigate# gcc test.c -o test
root@zth:~/newDigate# ./test 32.6 \(\(x-10.3\)*1.43+13.77\)*10.132
462.617
root@zth:~/newDigate#