文章目录
一、问题描述和基本要求
【问题描述】
设计一个简单的算术表达式计算器。
【基本要求】
实现标准整数类型的四则运算表达式的求值(包含括号,可多层嵌入).
【测试数据】
(30+270)/3-12 * 3
5+(9(62-37)+15)*6
要求自行设计非法表达式,进行程序测试,以保证程序的稳定运行。
【实现提示】
可以设计以下辅助函数
status isNumber(char ReadInChar); //视ReadInchar 是否是数字而返回 TRUE 或 FALSE 。
int TurnToInteger(char IntChar); // 将字符’0’.’9’ 转换为整数 9
二、问题分析和任务定义
1.数据结构的选取
本次项目,可以将算术表达式分为操作数和操作符两种字符,所以可以建立操作数栈s和操作符栈chs,分别用来存放操作数和操作符,并根据其优先级来计算表达式
2.问题分析
本次问题的要求是要能够实现混合的四则运算(包含括号,可多层嵌入),由于用户输入的是中缀表达式,但有时在语法扫描分析等应用场合一般都是采用后缀表达即逆波兰式来运算,所以可以先将中缀转换成后缀,然后再进行运算。同时要注意异常处理。
3.功能分析
(1)能够将中缀转换成后缀,并存通过fstream存入文件中
(2)能判断符号优先级大小
(3)能实现 先乘除,后加减;从左算到右;先括号内,后括号外;
三、逻辑设计
1.变量类型说明
SeqStack类 | data(存放栈元素)、top(栈顶指针) |
---|---|
Calculator类 | s(操作数栈)、chs(操作符栈) |
2.变量说明
data[] | 存放栈元素 |
---|---|
top | 栈顶指针 |
SeqStack s | 操作数栈 |
SeqStack chs | 操作符栈 |
3.函数说明
push() | 压栈 |
---|---|
pop() | 弹出栈顶元素 |
getpop() | 取栈顶元素 |
empty() | 判断栈是否为空 |
top() | 返回栈顶值 |
Get() | 获取操作数栈顶的两个元素 |
Do() | 将操作数栈顶的两个元素做oper运算 |
run() | 从文件读取后缀表达式并计算 |
convert() | 将中缀表达式转换成后缀(逆波兰式),并存入文件 |
instack() | 栈内优先级 |
outstack() | 栈外优先级 |
DoIt() | 计算并输入结果 |
四、物理设计
1.存储结构
1.定义栈SeqStack类,并将函数放入头文件中
2.定义Calculator类,并在其中建立了两个栈,用来将表达式转换成后缀,并计算结果
2.模块划分
1.主函数模块
2.Calculator类模块
3.栈内优先级模块
4.栈外优先级模块
5.中缀转后缀并计算模块
3.函数设计
1.SeqStack类:data(存放栈元素)、top(栈顶指针)
2.Calculator类:s(操作数栈)、chs(操作符栈)
3.push(){压栈}
4.pop() {弹出栈顶元素}
5.getpop(){取栈顶元素}
6.empty(){判断栈是否为空}
7.top(){返回栈顶值}
8.Get(){获取操作数栈顶的两个元素}
9.Do(){将操作数栈顶的两个元素做oper运算}
10.run(){从文件读取后缀表达式并计算}
11.convert(){将中缀表达式转换成后缀(逆波兰式),并存入文件}
12.instack(){栈内优先级}
13.outstack(){栈外优先级}
14.DoIt(){计算并输出结果}
五、测试数据
(30+270)/3-123
5+(9*(62-37)+15)*6
要求自行设计非法表达式,进行程序测试,以保证程序的稳定运行。
六、函数具体实现
1.Get(double& opd1, double& opd2)
void Calculator::GetOperand(double& opd1, double& opd2)
{
opd2 = s.pop();
opd1 = s.pop();
}
2.Do(char oper)
void Calculator::DoOperand(char oper) //Oper运算
{
double opd1, opd2;
GetOperand(opd1, opd2);
switch (oper)
{
case '+':
s.push(opd1 + opd2);
break;
case '-':
s.push(opd1 - opd2);
break;
case '*':
s.push(opd1 * opd2);
break;
case '/':
if (fabs(opd2) < 1e-6)
{
cout << "除数不能为0" << endl; //若除数为0,则发出警告并退出程序
exit(0);
}
else s.push(opd1 / opd2);
break;
case '^':
s.push(pow(opd1, opd2));
break;
}
}
3.run()
void Calculator::run()
{
char c; double newopd;
ifstream infile("D:/data.txt"); //读取文件中的后缀表达式
if (!infile)
{
cout << "未能打开文件" << endl;
return;
}
while (infile.get(c), c != '#')
{
switch (c)
{
case '+':
case '-':
case '*':
case '/':
case '^':
DoOperand(c);
break;
case '|':
infile >> newopd;
s.push(newopd);
break;
default:
cout << "输入的算术表达式有误" << endl; //异常处理,若表达式有误,直接退出程序
exit(0);
return;
}
}
if (!s.empty())
{
cout << s.pop() << endl;
}
infile.close();
}
4.instack(char ch)
int Calculator::inp(char ch) //栈内优先级
{
switch (ch)
{
case '(':
return 1;
case '^':
return 4;
break;
case '*':
return 3;
break;
case '/':
return 3;
break;
case '+':
return 2;
break;
case '-':
return 2;
break;
case '#':
return 0;
break;
default:
cout << "输入的算术表达式有误!" << endl; //异常处理,若表达式有误,直接退出程序
exit(0);
return -1;
}
}
5.outstack(char ch)
int Calculator::outp(char ch) //栈外优先级
{
switch (ch)
{
case '(':
return 5;
break;
case '^':
return 4;
break;
case '*':
return 3;
break;
case '/':
return 3;
break;
case '+':
return 2;
break;
case '-':
return 2;
break;
case '#':
return 0;
break;
default:
cout << "输入的算术表达式有误!" << endl;
exit(0);
return -1;
}
}
6.convert()
void Calculator::convert()//中缀转后缀(逆波兰式),并存入文件
{
char ch, y; double newopd;
chs.push('#');
ofstream outfile("D:/data.txt");
while (cin.get(ch), ch != '=')
{
if (isdigit(ch))
{
cin.putback(ch);
cin >> newopd;
outfile << '|' << newopd;
}
else if (ch == '.')
{
cin.putback(ch);
cin >> newopd;
outfile << '|' << newopd;
}
else if (ch == ')')
{
for (y = chs.pop(); y != '('; y = chs.pop())
outfile << y;
}
else
{
y = chs.getpop();
if (outstack(ch) > instack(y))
chs.push(ch);
else
{
chs.pop();
outfile << y;
y = chs.getpop();
while (outstack(ch) <= instack(y))
{
chs.pop();
outfile << y;
y = chs.getpop();
}
chs.push(ch);
}
}
}
while (!chs.empty())
{
y = chs.pop();
outfile << y;
}
outfile.close();
}
7.DoIt()
void Calculator::DoIt()
{
char flag;
cout << "请输入表达式(末尾加=):" << endl;
cin >> flag;
cin.putback(flag);
convert();
cout << "表达式的运算结果:" << endl;
run();
}
以上代码均能ac。