今天我们来利用栈来实现一下表达式的求值。
之前我也写过关于栈的基本功能的内容,链接如下:
什么是“表达式求值“
表达式的求值,简单来说,就是实现任意加减乘除与括号的表达式求值,以文本的文件的方式来输入输出。
最终效果演示
1.这里我们需要先将未计算的表达式写入begin.txt文本
2.运行程序
.
3.查看finall.txt文本
实现代码
求取算数表达式的值,我们将这个过程分成两步:
1.将算数表达式exp转化为后缀表达式postexp。
2.使用后缀表达式postexp求值。
将算数表达式exp转化为后缀表达式postexp
在转换过程中,我们采用一个运算符栈,用来存储运算符结构如下:
struct
{
char *data;
int top;
}op;
将中缀表达式exp转化为后缀表达式postexp的过程为:
while(从exp读取字符ch,ch!='\0')
{
if ch 为数字,将后续的所有数字均以次存放在postexp中,并以字符“#”标志数值串结束。
if ch 为'(',则将括号进栈到运算符栈op中
if ch 为')',则将运算符op中左括号右边的运算符依次出栈并放在postexp中,并将左括号删除。
if ch 为 运算符 ,当ch的优先级不大于栈顶运算符(栈顶元素为"("除外)的优先级,则以此出栈并存入postexp中,然后将ch进栈。
}
当字符串exp扫描完毕时,则将运算符栈op中的所有运算符以此出栈并以此出栈存放到postexp,最后得到后缀表达式postexp.
这里给出一个例子,以便理解:
将exp转化为后缀表达式postexp的trans()算法如下。
void trans(char exp[], char postexp[])
{
char ch;
op.data = new char[MAXSIZE];
op.top = -1;
int i = 0, j = 0;//i为exp下标,j为postexp下标
ch = exp[i];
//cout << ch<<"_";
i = 1;
//cout << ch << "_";
while (ch!='\0')//扫描字符数组exp
{
switch (ch)
{
case '(': //左括号直接入栈
op.top++;
op.data[op.top] = ch;
break;
case ')':
while (op.data[op.top] != '(') //‘(’之前的字符以此出栈并存入postexp
{
postexp[j] = op.data[op.top];
j++;
op.top--;
}
op.top--; //左括号出栈,并删除
break;
case'+': //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
case'-':
//cout << op.top << "_";
//if ((exp[0] == '+'|| exp[0] == '-')&&(op.top==-1))//当首运算符为+或-
//{
// postexp[j] = 0;
// postexp[++j] = '#';
// j++;
//}
while (op.top != -1 && op.data[op.top] != '(')//依次出栈,存入postexp
{
postexp[j] = op.data[op.top];
j++;
op.top--;
//cout << op.top << "_";
}
op.top++; //将ch进栈
//cout << op.top<<"_";
op.data[op.top] = ch;
break;
case'*': //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
case'/':
while (op.top != -1 && op.data[op.top] != '('&&
(op.data[op.top]=='*'||op.data[op.top]=='/'))
{
postexp[j] = op.data[op.top];//依次出栈,存入postexp
j++;
op.top--;
}
op.top++;//将ch进栈
op.data[op.top]=ch;
break;
case' ': //将空格过滤
break;
default:
while (ch >= '0' && ch <= '9') //判定为数字
{
postexp[j] = ch;
j++;
ch = exp[i];
i++;
}
i--;
postexp[j] = '#'; //在每段数字结束后加‘#’作为结束符
j++;
}
ch = exp[i];
i++;
}
while (op.top != -1)
{
postexp[j] = op.data[op.top];
j++;
op.top--;
}
postexp[j] = '\0';
}
使用后缀表达式postexp求值
在计算后缀表达式值得过程中使用中使用一个数值栈st,设计其结构如下:
struct
{
float* data;
int top;
}st;
求值过程如下:
while(从postexp读取字符ch,ch!='\0')
{
if ch 为数字,将后继的所有数字构成一个整数存放在数值栈st中
if ch 为“+”,则从数值栈st中退站两个运算数,相加后进栈st中
if ch 为“-”,则从数值栈st中退站两个运算数,相减后进栈st中
if ch 为“*”,则从数值栈st中退站两个运算数,相乘后进栈st中
if ch 为“/”,则从数值栈st中退站两个运算数,相除后进栈st中(若除数为0,则提示错误)
}
我们将上一个例子得到的后缀表达式求值
根据上述原理得到compvalue算法
float compvalue(char postexp[])
{
float d;
char ch;
int i = 0;
st.data = new float[MAXSIZE];
st.top = -1;
ch = postexp[i];
//cout << ch <<"_"<< endl;;
i++;
while (ch != '\0')
{
switch (ch)
{
case'+':
st.data[st.top - 1] = st.data[st.top - 1] + st.data[st.top];
st.top--;
break;
case'-':
st.data[st.top - 1] = st.data[st.top - 1] - st.data[st.top];
st.top--;
break;
case'*':
st.data[st.top - 1] = st.data[st.top - 1] * st.data[st.top];
st.top--;
break;
case'/':
if(st.data[st.top]!=0)
st.data[st.top - 1] = st.data[st.top - 1] / st.data[st.top];
else
{
cout << ("\n\t除零错误!\n");
exit(0);
}
st.top--;
break;
case' ':
break;
default:
d = 0;
while (ch >= '0' && ch <= '9')
{
d = 10 * d + ch - '0';
ch = postexp[i];
i++;
}
st.top++;
st.data[st.top] = d;
}
ch = postexp[i];
i++;
}
return st.data[st.top];
}
以上就是表达式求值的核心部分。
接下来是完整的代码
1.头文件 calulate.h
#pragma once
#include<iostream>
using namespace std;
#include<fstream>
#include<string>
#include<string.h>
#define MAXSIZE 100
struct
{
char *data;
int top;
}op;
struct
{
float* data;
int top;
}st;
string read(int k);
void trans(char exp[], char postexp[]);
float compvalue(char postexp[]);
void Print(char over[]);
2.源文件calulate.cpp
#include"calulate.h"
void Init()
{
}
string read(int k)
{
fstream ofs;
ofs.open("begin.txt", ios::in|ios::out);
if (!ofs.is_open())
{
cout << "文件打开失败!" << endl;
}
string data[100];
int i = 0;
while (getline(ofs, data[i]))
{
i++;
}
return data[k];
}
void trans(char exp[], char postexp[])
{
char ch;
op.data = new char[MAXSIZE];
op.top = -1;
int i = 0, j = 0;//i为exp下标,j为postexp下标
ch = exp[i];
//cout << ch<<"_";
i = 1;
//cout << ch << "_";
while (ch!='\0')//扫描字符数组exp
{
switch (ch)
{
case '(': //左括号直接入栈
op.top++;
op.data[op.top] = ch;
break;
case ')':
while (op.data[op.top] != '(') //‘(’之前的字符以此出栈并存入postexp
{
postexp[j] = op.data[op.top];
j++;
op.top--;
}
op.top--; //左括号出栈,并删除
break;
case'+': //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
case'-':
//cout << op.top << "_";
//if ((exp[0] == '+'|| exp[0] == '-')&&(op.top==-1))//当首运算符为+或-
//{
// postexp[j] = 0;
// postexp[++j] = '#';
// j++;
//}
while (op.top != -1 && op.data[op.top] != '(')//依次出栈,存入postexp
{
postexp[j] = op.data[op.top];
j++;
op.top--;
//cout << op.top << "_";
}
op.top++; //将ch进栈
//cout << op.top<<"_";
op.data[op.top] = ch;
break;
case'*': //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
case'/':
while (op.top != -1 && op.data[op.top] != '('&&
(op.data[op.top]=='*'||op.data[op.top]=='/'))
{
postexp[j] = op.data[op.top];//依次出栈,存入postexp
j++;
op.top--;
}
op.top++;//将ch进栈
op.data[op.top]=ch;
break;
case' ': //将空格过滤
break;
default:
while (ch >= '0' && ch <= '9') //判定为数字
{
postexp[j] = ch;
j++;
ch = exp[i];
i++;
}
i--;
postexp[j] = '#'; //在每段数字结束后加‘#’作为结束符
j++;
}
ch = exp[i];
i++;
}
while (op.top != -1)
{
postexp[j] = op.data[op.top];
j++;
op.top--;
}
postexp[j] = '\0';
}
float compvalue(char postexp[])
{
float d;
char ch;
int i = 0;
st.data = new float[MAXSIZE];
st.top = -1;
ch = postexp[i];
//cout << ch <<"_"<< endl;;
i++;
while (ch != '\0')
{
switch (ch)
{
case'+':
st.data[st.top - 1] = st.data[st.top - 1] + st.data[st.top];
st.top--;
break;
case'-':
st.data[st.top - 1] = st.data[st.top - 1] - st.data[st.top];
st.top--;
break;
case'*':
st.data[st.top - 1] = st.data[st.top - 1] * st.data[st.top];
st.top--;
break;
case'/':
if(st.data[st.top]!=0)
st.data[st.top - 1] = st.data[st.top - 1] / st.data[st.top];
else
{
cout << ("\n\t除零错误!\n");
exit(0);
}
st.top--;
break;
case' ':
break;
default:
d = 0;
while (ch >= '0' && ch <= '9')
{
d = 10 * d + ch - '0';
ch = postexp[i];
i++;
}
st.top++;
st.data[st.top] = d;
}
ch = postexp[i];
i++;
}
return st.data[st.top];
}
3.测试文件test.cpp
#include"calulate.h"
void test()
{
fstream fst;
fst.open("final.txt", ios::app);
char*postexp;
postexp = new char[20];
int i = 0;
int j = 0;
float tiv=0;
string s;
char mine[20]="0";
for (i = 0; i < 20; i++)
{
if (read(i) != "")
{
s = read(i);
strncpy_s(mine, s.c_str(), s.length()-1);
//当首元素为运算符时
if (mine[0] == '-'||mine[0]=='+')
{
for (int m = 18; m > -1; m=m - 1)
{
mine[m + 1] = mine[m];
}
mine[0] = '0';
}
//当出现(-x)时
for (int y = 1; y < 20; y++)
{
if (mine[y] == '-'&&mine[y-1]=='(')
{
for (int w = 18; w > y - 1; w = w - 1)
{
mine[w + 1] = mine[w];
}
mine[y] = '0';
}
}
trans(mine, postexp);
//char postexp[20] = "0#1#-2#+";
tiv=compvalue(postexp);
//cout <<endl<< postexp;
if (mine[0] != '0')
{
for (j = 0; mine[j];j++)
{
cout << mine[j];
fst << mine[j];
}
}
if(mine[0]=='0')
{
for (j = 1; mine[j]; j++)
{
cout << mine[j];
fst << mine[j];
}
}
cout <<"="<< tiv << endl;
fst << "=" << tiv << endl;
}
}
}
int main()
{
test();
return 0;
}