Warning: file_get_contents(https://whois.pconline.com.cn/jsLabel.jsp?ip=127.0.0.1) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.1 503 Service Temporarily Unavailable in D:\wwwroot\huidong\wwwroot\function.inc.php on line 884
程序∮汇东表达式计算器 - huidong

huidong

首页 | 会员登录 | 关于争取 2022 寒假做出汇东网 Ver3.0.0 !
搜索文章


通过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.整体的步骤

在开始写代码之前,整理一下整个程序的思路(变量名称是源码的):

  1. 获取用户输入到chInPut

  2. 进行预处理:判断算式开头是不是负号,是的话在temp_Stack开头添0

  3. 将数字,符号放入temp_Stack,’{‘和’[‘作为’(‘放入temp_Stack,’}‘和’]‘作为’)'放入temp_Stack

  4. 由于chInPut是char型数组,所以每个数或符号都在一个区域内,所以如果有多位数,要进行合并

  5. 预处理结束,开始计算。循环判断temp_Stack的第n个值是什么:

  6. 若是数字,加减号和左括号,放入Stack

  7. 若是乘除号,先判断下一个是不是数,是数的话算掉,不是数就先放入Stack

  8. 若是右括号,循环将括号内的值放入pNode中计算,计算完后得出的结果代替整个括号,判断Stack[top-2]是不是乘除号,若是,进行计算

  9. 判断完整个temp_Stack后,乘除号和括号都已算完。判断现在的Stack中是否只剩下一个数,是的话直接显示答案,就是这个数,如果不止一个数,继续计算

  10. 循环将栈顶和栈顶前三个字符提取到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;
	}
}

效果预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述




返回首页


Copyright (C) 2018-2024 huidong