通过C自己实现分析表达式并计算得出结果,做出一个可以计算长式子的计算器,并将此功能整合成了dll,lib库提供计算功能的支持。
该项目是开源的。在下面有提到。
你可以输入像"12*6+(7-4/(2*6-8)*3)*(44-67)"或像"19-65*(26-(6+58*3)-7*2)+73"的复杂算式,它将迅速进行计算并得出答案!
计算器下载:
V1.0
在V1.0中,支持整形算式的计算;
汇东表达式计算器.rar
V2.0
在V2.0中,支持浮点型的计算;
汇东表达式计算器2.0安装程序.rar
V3.0
在V3.0中,界面全新改版为图形界面,其核心代码延续V2.0。
汇东表达式计算器3.0安装程序.rar
V3.1
V3.1对部分细节进行了优化。
汇东表达式计算器3.1安装程序.rar
Dll,Lib库下载
V1.0
HuiDongExpressionDLL_V1.0.rar
V2.0
HuiDongExpressionDLL_V2.0.rar
调用方式(C)
V1.0
#pragma comment(lib,"HuiDongExpressionDLL_V1.0.lib")
extern "C" __declspec(dllimport) int Expression(char chGet[512]);
V2.0
#pragma comment(lib,"HuiDongExpressionDLL_V2.0.lib")
extern "C" __declspec(dllimport) double Expression(char chGet[512]);
chGet参数为要计算的算式。
源码
介绍如何通过栈来实现计算算式。
首先
我们要通过多个栈对算式进行操作和计算得出答案,在计算前,我们需要先明确整个计算的流程:
而这其中,至关重要的便是计算这部分了,继续看计算的原理:
通过栈计算算式的原理
1.优先级高的先算
以算式9+8*2-(3+1)为例
正常计算时,应该先乘除后加减,括号内的要先算。那么就是:
9+8*2-(3+1)
=9+16-4
计算完优先级高的之后,再计算优先级低的。就是:
9+16-4
=21
为了让电脑能够计算出9+8*2-(3+1)=21
,我们现在可以开始使用栈。
根据栈的特性(后入先出),我们可以把算式中的字符逐个导入栈,然后根据算数的优先级,先算掉乘除和括号,然后留下加减运算,再把加减算掉,就可以了。
那么,应该怎么做呢?
首先,要创建一个数组来模拟栈,像:
char Stack[512];
但是,算式中有运算符和数字,怎么区分他们呢?
我们可以定义一个结构体,像:
struct Stack
{
int who; //存储存储在单个结构体内的数据类型是数字(int)还是运算符(char),
//0为数字(Int),1为运算符(char)
int num; //存储数字
char word; //存储运算符
}
假如有一个数字3,那么它放在结构体内,就是:
struct Stack A; //创建一个结构体A
A.who = 0; //类型为0,即数字
A.num = 3; //数字的值是3
A.word = NULL; //因为不是字符,存储字符的变量可以是空
再假如是运算符+,那么它在结构体里就是:
struct Stack B; //创建一个结构体B
B.who = 1; //类型为1,即符号
B.num = 0; //因为不是数字,存储数字的变量可以是空
B.word = '+'; //字符是+
那又因为我们需要一个栈,栈要有长度,我们可以创建一个结构体数组来模拟栈:
struct Stack compute[512];
我们同样需要一个int变量来存储现在栈顶的位置:
int top = 0;
在使用栈之前,应该要对栈进行初始化,即清空:
for(int i=0;i<512;i++)
{
compute[i].who = 0;
compute[i].num = 0;
compute[i].word = NULL;
}
有了栈,就可以继续下一步,将算式导入栈了。循环判断算式9+8*2-(3+1)
的第n个字符是数字还是符号,再存储到栈中。
//存储用户输入的算式的数组是chInPut,原先是char型的
//存储算式长度的变量是nLong
//循环判断chInPut[n]是数字还是符号然后分类存入栈中
for(int n=0;n<nLong;n++)
{
//如果chInPut的第n个是数字的话
if('0'<=chInPut[n]&&chInPut[n]<='9')
{
//定义的临时变量ch,用于转换类型
char ch[2];
//数组chInPut原先是char型的,所以要把其中的值存储到栈中的int变量的话,要使用atoi函数进行类型转换后再存储进栈内
ch[0] = chInPut[n];
//定义的临时变量temp用于存储atoi的结果
int temp = atoi(ch);
//将这个数字的信息存储到栈顶,就是入栈。
//字符类型:数字
compute[top].who = 0;
//将temp中的值入栈
compute[top].num = temp;
//运算符,设不设置都可以,为空。
compute[top].word = NULL;
//既然新入栈了信息,那么栈顶的位置也要相应的增加
top++;
}
//如果chInPut的第n个是字符的话
else
{
//要是是乘除号,要先算
if(chInPut[n] == '*')
{
//计算...
}
else if(chInPut[n] == '/')
{
//计算...
}
//不是乘除号,还有可能是加减号和括号
else if(chInPut[n] == '+' || chInPut[n] == '-' || chInPut[n] == '(')
{
//入栈...先不计算,因为要先算乘除和括号
//为什么碰到左括号也先入栈呢?因为碰到左括号的时候,这个括号里的信息都还没有入栈,要等碰到右括号的时候,把已入栈的括号里的算式取出来算
}
//是右括号
else if(chInPut[n] == ')')
{
//计算括号内的算式...然后再从栈顶弹出,就是出栈,把整个括号都出栈,再将括号内的算式算出来的答案入栈,这样就以一个数代替了括号
}
}
}
这样,循环完毕的时候,就会只剩下像9+16-4
一样的算式,或者只剩下一个数了。因为在循环中,已经把乘除和括号先算完了。还有,代码中的入栈代码很长,可以将出入栈的代码封装成函数,这样出入栈就方便了,这个函数像是:
//入栈函数
void push(int in_who,int in_num,char in_word)
{
compute[top].who = in_who;
compute[top].num = in_num;
compute[top].word = in_word;
top++;
}
//出栈函数
void pop()
{
compute[top-1].who = 0;
compute[top-1].num = 0;
compute[top-1].word = NULL;
top--;
}
2.剩下的一点…加减混合运算怎么办
因为在刚刚的循环中,把乘除和括号算完了,那么就可能只剩下一个只有加或是减或是加减混合运算的式子,如果说用户给出的算式只有乘除和括号运算的话,那么到这一步的时候,就只剩下一个数了。那么我们应该先判断是不是只剩下一个数了,如果是的话,输出这个数,因为它就是答案了。如果还有算式需要计算的话,那么,计算他们。
判断是不是只剩下一个数:
if(top == 1)
{
printf("答案是:%d",compute[0].num);
}
计算算式(见源代码):
while (top >= 1)
{
pNode[3-nPopNum] = pop(MAIN_STACK);
nPopNum++;
if(nPopNum == 4)
{
if( 1 == pNode[0].who )
{
if('-' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else if('+' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
push(MAIN_STACK,1,0,pNode[0].word);
push(MAIN_STACK,0,result.num,NULL);
nPopNum = 0;
}
}
if(nPopNum == 3)
{
if(pNode[2].word == '+')
{
result.num = pNode[1].num + pNode[3].num;
}
if(pNode[2].word == '-')
{
result.num = pNode[1].num - pNode[3].num;
}
//pop(MAIN_STACK);
push(MAIN_STACK,0,result.num,NULL);
}
else
{
//有问题
printf("MISS BUG!\n");
}
3.整体的步骤
在开始写代码之前,整理一下整个程序的思路(变量名称是源码的):
获取用户输入到chInPut
进行预处理:判断算式开头是不是负号,是的话在temp_Stack开头添0
将数字,符号放入temp_Stack,’{‘和’[‘作为’(‘放入temp_Stack,’}‘和’]‘作为’)'放入temp_Stack
由于chInPut是char型数组,所以每个数或符号都在一个区域内,所以如果有多位数,要进行合并
预处理结束,开始计算。循环判断temp_Stack的第n个值是什么:
若是数字,加减号和左括号,放入Stack
若是乘除号,先判断下一个是不是数,是数的话算掉,不是数就先放入Stack
若是右括号,循环将括号内的值放入pNode中计算,计算完后得出的结果代替整个括号,判断Stack[top-2]是不是乘除号,若是,进行计算
判断完整个temp_Stack后,乘除号和括号都已算完。判断现在的Stack中是否只剩下一个数,是的话直接显示答案,就是这个数,如果不止一个数,继续计算
循环将栈顶和栈顶前三个字符提取到pNode进行计算,直至只能提取到3个字符,将这三个字符进行计算,得到最终答案,显示答案。
整体的大概步骤就像这10步。
完整源代码
// HuiDongExpression.cpp : 定义控制台应用程序的入口点。
//
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <conio.h>
#include <memory.h>
#include <string.h>
#define TEMP_STACK 0
#define MAIN_STACK 1
typedef struct bowl
{
int who; //存储存储在单个结构体内的数据类型是int还是char,
//0为Int,1为char
int num; //存储数字
char word; //存储字符
} BOWL;
//全局变量
BOWL temp_Stack[512]; //用于临时存放,预处理的时候用的
int temp_top; //存储上述栈顶
BOWL Stack[512]; //真正计算用的栈
int top; //存储上述栈顶
char chInPut[512] = {0}; //获取用户输入
int Result; //最终结果
int CS; //记录打印次数
void push(int NAME,int in_who,int in_num,char in_word)
{
if(NAME == TEMP_STACK)
{
temp_Stack[temp_top].who = in_who;
temp_Stack[temp_top].num = in_num;
temp_Stack[temp_top].word = in_word;
temp_top++;
}
else if(NAME == MAIN_STACK)
{
Stack[top].who = in_who;
Stack[top].num = in_num;
Stack[top].word = in_word;
top++;
}
}
BOWL pop(int NAME)
{
BOWL node;
if(NAME == TEMP_STACK)
{
node = temp_Stack[temp_top-1];
temp_Stack[temp_top-1].who = 0;
temp_Stack[temp_top-1].num = 0;
temp_Stack[temp_top-1].word = NULL;
temp_top--;
}
else if(NAME == MAIN_STACK)
{
node = Stack[top-1];
Stack[top-1].who = 0;
Stack[top-1].num = 0;
Stack[top-1].word = NULL;
top--;
}
return node;
}
void init_Stack()//初始化栈及对用户输入的预处理
{
//清空Stack
int i;
temp_top = 0;
top = 0;
for(i=0;i<512;i++)
{
Stack[i].num = 0;
Stack[i].who = 0;
Stack[i].word = NULL;
temp_Stack[i].num = 0;
temp_Stack[i].who = 0;
temp_Stack[i].word = NULL;
}
//开始读用户输入,进行预处理
int j;
if(chInPut[0] == '-')//开头是负号
{
push(TEMP_STACK,0,0,NULL);
}
int Converge = 0; //用于累计以存放多位数
for(j=0;j<512;j++)
{
if('0'<=chInPut[j]&&chInPut[j]<='9')//如果是数字
{
if('0'<=chInPut[j+1]&&chInPut[j+1]<='9')//这个数是两位以上,因为下一个也是数
{
if(!('0'<=chInPut[j-1]&&chInPut[j-1]<='9'))//这只是这个两位以上的数的开头
{
char ch[2];
ch[0] = chInPut[j];
Converge = atoi(ch);//先就直接放进去
continue;
}
int n;
char ch[2];
ch[0] = chInPut[j];
n = atoi(ch);
Converge*=10;
Converge+=n;
}
else if(('0'<=chInPut[j-1]&&chInPut[j-1]<='9')&&!('0'<=chInPut[j+1]&&chInPut[j+1]<='9'))//这个数是两位以上的一个数的结尾
{
int n;
char ch[2];
ch[0] = chInPut[j];
n = atoi(ch);
Converge*=10;
Converge+=n;
push(TEMP_STACK,0,Converge,NULL);
Converge = 0;//清零
}
else//这只是个位数
{
char ch[2];
ch[0] = chInPut[j];
int temp_into = atoi(ch);
push(TEMP_STACK,0,temp_into,NULL);
}
}
else if((chInPut[j] == '+')||(chInPut[j] == '-')||(chInPut[j] == '*')
||(chInPut[j] == '/')||(chInPut[j] == ')'))//是符号
{
push(TEMP_STACK,1,0,chInPut[j]);
}
//一切的括号都必须是小括号,如遇中大括号,转换为小括号
else if(chInPut[j] == '('||chInPut[j] == '['||chInPut[j] == '{')//是左括号,要确认一下是不是以负号开头
{
push(TEMP_STACK,1,0,'(');
if(chInPut[j+1] == '-')
{
push(TEMP_STACK,0,0,NULL);
}
}
else if(chInPut[j] == ']'||chInPut[j] == '}')
{
push(TEMP_STACK,1,0,')');
}
}
}
void count() //开始计算
{
int progress;//指向现在计算到的地方
for(progress=0;progress<temp_top;progress++)
{
if((temp_Stack[progress].who == 1)&&(temp_Stack[progress].word == '*'))//碰上乘号
{
if(temp_Stack[progress+1].who == 0)//乘号后面是数字
{
int C1 = Stack[top-1].num;
int C2 = temp_Stack[progress+1].num;
int J = C1*C2;
pop(MAIN_STACK);
push(MAIN_STACK,0,J,NULL);
progress++;
continue;
}
else
{
push(MAIN_STACK,1,0,'*');
}
}
else if((temp_Stack[progress].who == 1)&&(temp_Stack[progress].word == '/'))//碰上除号
{
if(temp_Stack[progress+1].who == 0)//除号后面是数字
{
int C1 = Stack[top-1].num;
int C2 = temp_Stack[progress+1].num;
int J = C1/C2;
//由于可能会出现余数,有余数的情况要提示
int ys = C1%C2;
if(ys != 0)
{
printf("余数已忽略");
}
pop(MAIN_STACK);
push(MAIN_STACK,0,J,NULL);
progress++;
continue;
}
else
{
push(MAIN_STACK,1,0,'/');
}
}
else if((temp_Stack[progress].who == 1)&&(temp_Stack[progress].word == ')'))//碰上右括号
{
int J;
if((Stack[top-1].who == 0)&&(Stack[top-2].who == 1 && Stack[top-2].word == '('))//有一个特殊情况,就是括号内已经只剩下数字
{
//如果括号内只剩下数字,那么就把数字提出,去掉括号,再将数字放回
int shift;
shift = Stack[top-1].num;
pop(MAIN_STACK);
pop(MAIN_STACK);
push(MAIN_STACK,0,shift,NULL);
goto Checkpoints;//直接跳转到乘除号检查点
}
int k;
BOWL result;//计算的结果
int nPopNum = 0;//出栈计数
BOWL pNode[4];//保存出栈的数据
//初始化
for(k=0;k<4;k++)
{
pNode[k].who = 0;
pNode[k].num = 0;
pNode[k].word = NULL;
}
result.who = 0;
result.num = 0;
result.word = NULL;
while(!((Stack[top - 1].who == 1)&&(Stack[top - 1].word == '(')))//碰到左括号之前,while循环成立
{
pNode[3-nPopNum] = pop(MAIN_STACK);
nPopNum++;
if(nPopNum == 4)
{
if( 1 == pNode[0].who )
{
if('-' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else
{
printf("MISS BUG!\n");
//表达式错误
break;
}
}
else
{
printf("MISS BUG!\n");
//表达式错误
break;
}
}
else if('+' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else
{
printf("MISS BUG!\n");
//表达式错误
break;
}
}
else
{
printf("MISS BUG!\n");
//表达式错误
break;
}
}
else
{
printf("MISS BUG!\n");
//表达式错误
break;
}
}
push(MAIN_STACK,1,0,pNode[0].word);
push(MAIN_STACK,0,result.num,NULL);
nPopNum = 0;
}
}
if(nPopNum == 3)
{
if(pNode[2].word == '+')
{
result.num = pNode[1].num + pNode[3].num;
}
if(pNode[2].word == '-')
{
result.num = pNode[1].num - pNode[3].num;
}
pop(MAIN_STACK);
push(MAIN_STACK,0,result.num,NULL);
}
else
{
//有问题
printf("MISS BUG!\n");
}
Checkpoints:
if(Stack[top-2].word == '*' || Stack[top-2].word == '/')//再次确认一下前面是不是乘除号
{
if(Stack[top-2].word == '*')
{
J = Stack[top-3].num * Stack[top-1].num;
}
else if(Stack[top-2].word == '/')
{
J = Stack[top-3].num / Stack[top-1].num;
}
pop(MAIN_STACK);
pop(MAIN_STACK);
pop(MAIN_STACK);
push(MAIN_STACK,0,J,NULL);
}
}
else//如果是数字和加减号,直接放进栈里就好,最后一起计算。
{
if (temp_Stack[progress].who == 0)
{
push(MAIN_STACK,0,temp_Stack[progress].num,NULL);
}
else if (temp_Stack[progress].who == 1)
{
push(MAIN_STACK,1,0,temp_Stack[progress].word);
}
}
}
//最后的一点计算
int k;
BOWL result;//计算的结果
int nPopNum = 0;//出栈计数
BOWL pNode[4];//保存出栈的数据
//初始化
for(k=0;k<4;k++)
{
pNode[k].who = 0;
pNode[k].num = 0;
pNode[k].word = NULL;
}
result.who = 0;
result.num = 0;
result.word = NULL;
//如果只剩下一个数
if(top == 1 && Stack[top].who == 0)
{
goto last;
}
while (top >= 1)
{
//此处要参照中间处理括号的方法重写
pNode[3-nPopNum] = pop(MAIN_STACK);
nPopNum++;
if(nPopNum == 4)
{
if( 1 == pNode[0].who )
{
if('-' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else if('+' == pNode[0].word)
{
if((1 == pNode[2].who) &&(0 == pNode[1].who) && (0 == pNode[3].who))
{
if('+' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num + pNode[3].num;
}
else if('-' == pNode[2].word)
{
result.who = 0;
result.num = pNode[1].num - pNode[3].num;
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
else
{
//表达式错误
printf("MISS BUG!\n");
break;
}
}
push(MAIN_STACK,1,0,pNode[0].word);
push(MAIN_STACK,0,result.num,NULL);
nPopNum = 0;
}
}
if(nPopNum == 3)
{
if(pNode[2].word == '+')
{
result.num = pNode[1].num + pNode[3].num;
}
if(pNode[2].word == '-')
{
result.num = pNode[1].num - pNode[3].num;
}
//pop(MAIN_STACK);
push(MAIN_STACK,0,result.num,NULL);
}
else
{
//有问题
printf("MISS BUG!\n");
}
last:
Result = Stack[0].num;
}
int _tmain()
{
printf("汇东表达式计算器 Ver1.0\n官网:www.huidong.xyz\n请输入你的表达式(要求表达式要正确):\n");
gets_s(chInPut);
system("cls");
system("title 汇东表达式计算器");
CS = 0;
init_Stack();
count();
printf("\n原算式:");
puts(chInPut);
printf("\n处理后算式:");
for(int i=0;i<temp_top;i++)
{
if(temp_Stack[i].who == 0)
{
printf("%d",temp_Stack[i].num);
}
else if(temp_Stack[i].who == 1)
{
printf("%c",temp_Stack[i].word);
}
}
printf("\n计算结果:%d\n按R结束...\n",Result);
while(true)
{
char ch = _getch();
if(ch == 'R' || ch == 'r')
return 0;
}
}
效果预览