表达式计算
来源:互联网 发布:重庆北碚网络花店 编辑:程序博客网 时间:2024/05/16 15:52
三个步骤:读入,分离数据与符号,计算。
读入
字符串输入可以用scanf()和gets()完成。scanf("%s")遇到空格就中断,而输入中可能含有空格,因此放弃scanf()。gets()只有在遇到EOF和换行符的时候,才会停止读入,并不进行边界检查,所以当字符数组大小小于输入的字符串时,会导致溢出。同样有这个问题的还有strcpy(),strcat(),sprintf(),scanf(),sscanf(),fscanf(),vfscanf(),vsprintf(),vscanf(),vsscanf(),streadd(),strecpy(),strtrns()等。改进方法是使用fgets()替代gets()。
char *fgets(char *buf, //用来存储所得数据的地址的指针int bufsize, //读入数据的大小FILE *stream) //将要读取的文件流的指针
fgets()是一个从文件里读入的函数,由于这里是直接输入,所以第三个参数是stdin(这是stdio.h里规定的),第二个参数直接用开出的字符数组长度,第一个就是数组头指针了。
分离数据
stdlib.h里有一系列类型转换的函数,strtod,strtof,strtod,strtol,strtoll,strtoul,strtoull等。这里可以使用的是strtod,将字符串转化为double型数据。
double strtod(const char *nptr, //字符串地址char **endptr); //读完后指向剩余的第一个字符函数会跳过nptr所指向的字符串中的空白字符,然后把后续字符都转换成为double型的值。如果endptr不是空指针,那么strtod就修改endptr指向的对象,从而使endptr指向第一个剩余字符。如果没有发现double型的值,或者有错误的格式,那么strtod函数把nptr存储到endptr指向的对象中。如果要表示的数过大或者过小,函数就把ERANGE存储到errno中。
分离符号
直接用switch语句就可以了。
一、只有加减
只有加减,计算顺序是从左至右,这时表达式总是以数字开始,数字和符号间隔分布。先得到第一个数字,然后进入循环,一次读一个符号一个数字,读完就计算当前答案,直到字符串结尾。流程:读第一个数字,(读运算符,读第二个数字,判断运算符做计算)括号内不断循环,直到字符串结尾。
/* 计算 */int Calculate( char expre[], int len ){ char operate, //运算符号 sign; //正负号 int num1, num2, //操作数 i = 0; /* 读入第一个数 */ num1 = 0; sign = 1; if ( '-' == expre[i] ) { sign = -1; ++i; } for ( i; expre[i] >= '0' && expre[i] <= '9'; ++i ) { num1 *= 10; num1 += expre[i] - '0'; } num1 *= sign; while ( i < len ) { operate = expre[i++]; /* 运算符号 */ /* 第二个数 */ num2 = 0; sign = 1; if ( '-' == expre[i] ) { sign = -1; ++i; } for ( i; expre[i] >= '0' && expre[i] <= '9'; ++i ) { num2 *= 10; num2 += expre[i] - '0'; } num2 *= sign; /* 运算 */ if ( '+' == operate ) { num1 = num1 + num2; } else if ( '-' == operate ) { num1 = num1 - num2; } } return num1;}
二、加上括号
注意到括号内仍然是一个表达式,因此用递归算出括号内子表达式的结果,左括号递归,右括号返回。括号可以迭代,函数递归也可以多层,但是注意括号配对。左括号左边可能有运算符号,右边一定是数字(也有可能有负号),因此判断左括号在读数字之前。同样,判断右括号在读数字之后。流程:
判断左括号,读第一个数字,(判断右括号,读运算符,判断左括号,读第二个数字,判断运算符做计算)括号内不断循环,直到字符串结尾。
int Calculate( char * begin ){ char operate, //运算符号 sign; //正负号 int num1, num2; //操作数 express = begin; /* 第一个数 */ num1 = 0; if ( '(' == *express ) { /* 左括号 */ num1 = Calculate( ++express ); /* 递归 */ } else { sign = 1; if ( '-' == *express ) { /* 负号 */ sign = -1; ++express; } while ( *express >= '0' && *express <= '9' ) { num1 = 10 * num1 + *(express++) - '0'; } num1 *= sign; } while ( *express ) { operate = *(express++); /* 运算符号 */ if ( ')' == *express ) { /* 遇到右括号返回 */ ++express; return num1; } /* 第二个数 */ num2 = 0; if ( '(' == *express ) { num2 = Calculate( ++express ); } else { sign = 1; if ( '-' == *express ) { sign = -1; ++express; } while ( *express >= '0' && *express <= '9' ) { num2 = 10 * num2 + *(express++) - '0'; } num2 *= sign; } /* 运算 */ if ( '+' == operate ) { num1 = num1 + num2; } else if ( '-' == operate ) { num1 = num1 - num2; } }<pre name="code" class="cpp"> return num1;}
三、有乘除运算
乘除是高级运算,且在当前是最高级,因此当遇到乘除号的时候,直接计算结果。加减是低级运算,遇到加减号的时候,还要检查后一个运算符:若是加减,则进行前面的计算;若是乘除,继续读下一个数字,先进行后面的计算。最长表达式类似于a+b*c,需要保存三个数字,两个运算符。流程:判断左括号,读第一个数字,(判断右括号,读运算符,判断左括号,读第二个数字,判断运算级别,级别高直接计算,级别低进入下一层循环(判断右括号,是则计算返回,否则继续读运算符,判断运算级别,低级进行左边运算,否则判断左括号,读第三个数字,做计算))两层循环,直到字符串结尾。这一阶段最难的部分就是判断优先级,情况很多,在不重复不遗漏的时候要注意简化代码,合并相同部分,尽量使得计算阶段放在一起
double Calculate( char * begin ){ char oper1, oper2; //运算符号 double num1, num2, num3; //操作数 express = begin; /* 第一个数 */ num1 = 0; if ( '(' == *express ) { /* 左括号递归 */ num1 = Calculate( ++express ); } if ( *express >= '0' && *express <= '9' || '-' == *express ) { num1 = GetAReal( express ); } while ( *express ) { if ( ')' == *express ) { /* 右括号返回 */ ++express; return num1; } oper1 = *(express++); /* 运算符号 */ /* 第二个数 */ num2 = 0; if ( '(' == *express ) { num2 = Calculate( ++express ); } if ( *express >= '0' && *express <= '9' || '-' == *express ) { num2 = GetAReal( express ); } /* 运算 */ /* 乘除直接计算 */ if ( '*' == oper1 ) { num1 *= num2; continue; } else if ( '/' == oper1 ) { num1 /= num2; continue; } /* 加减判断优先级 */ while ( *express ) { /* 在括号内直接计算返回 */ if ( ')' == *express ) { ++express; switch ( oper1 ) { case '+': return num1+num2; case '-': return num1-num2; case '*': return num1*num2; case '/': return num1/num2; default: printf("ERROR"); return 0; } } if ( '+' == *express || '-' == *express ) { if ( '+' == oper1 ) { num1 += num2; break; } else if ( '-' == oper1 ) { num1 -= num2; break; } } oper2 = *(express++); /* 运算符号 */ /* 第三个数 */ num3 = 0; if ( '(' == *express ) { num3 = Calculate( ++express ); } if ( *express >= '0' && *express <= '9' || '-' == *express ) { num3 = GetAReal( express ); } if ( '*' == oper2 ) { num2 *= num3; continue; } else if ( '/' == oper2 ) { num2 /= num3; continue; } } /* 到式子末尾 */ if ( !( *express )) { if ( '+' == oper1 ) { num1 += num2; continue; } else if ( '-' == oper1 ) { num1 -= num2; continue; } } } return num1;}
四、有更高级的运算
在前面已经观察到,仅仅两级运算加上括号,情况已经非常复杂,如果增加运算级别,情况会增多到难以编写,考虑使用数据结构简化分析过程。使用树保存表达式的结构,通过搜索计算出结果。也可以用栈,通过运算级别的不同实现入栈出栈,数据全部弹出的时候,就得到了结果。
用栈实现:
暂缺
建立一棵二叉树,树的深度不知,只能使用链式存储,每个结点有三个指针,指向父节点,左子结点和右子节点。树的叶结点存储数字,其余节点保存运算符号。先序遍历计算,计算完把值存在父节点中,删除父节点中的符号,左子结点和右子节点,剩最后一个结点时就是计算结果。
不足之处是容错性不好,对于输入要求很高,不过自己是够用了。
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算
- 计算表达式
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算
- 表达式计算 .
- 计算表达式
- 计算表达式
- 表达式计算
- 表达式计算
- 表达式计算
- ServletContextListener使用详解
- UVA488水题
- UVA11235 - Frequent values (QMQ问题)
- UVA445水题
- 黑马程序员_ReadLine_装饰设计模式_LineNumberReader
- 表达式计算
- OC面向对象—继承
- UVA 11488 Hyper Prefix Sets (Trie)
- C++笔试题深度分析 第三波 上
- Java语言基础
- 【计算几何】 POJ 1981 Circle and Points
- STL之iterator(迭代器)
- Select语句的基本操作
- 【Unity3D教程宝典之Shader篇:基础讲学习方法技巧 】