第五次作业(计算器第三步)
这里是代码传送门Calculator4.0
作业要求<1>
以下表达中, 用X代表第一个参数,用Y代表第二个参数(如果有的话),用Z代表第三个参数(如果有的话)
- 如果X为表达式,Y为空,那么只输出表达式的结果;
- 如果X为“-a”,Y表达式。将表达式输出,后面接着表达式的结果(中间用一个空格隔开);
- 如果X为“-f”,Y为txt文件的路径,则从该文件中读取表达式(每个表达式一行,有多行),并将答案输出到Z的路径上
Y的路径例如:“D:\test.txt”
Z的路径例如:“D:\results.txt”
- 所有关于输出的代码,都写在Print类里面
1.文件输入输出
前面的两个输入情况第四次作业已经考虑了,所以本次作业需要做的就是,在cmd命令下用文件输入输出。关于文件输入输出我并没有参考学习C++的文件输入输出,而是选用了自己不久前才学会并会经常使用的C的文件输入输出
freopen("in.txt“,"r",stdin);
freopen("out.txt","w",stdout);
因为后续freopen
会用的比较多,而且也是最近半个月才学会使用的,也算是完成了了解文件的读写这个作业的目的把。
注意点:
文件输入输出方式需要完成多组计算
我想到的方法是,用
while
循环输入,结束方式是处理到文件尾。
在我进行数据测试的时候,比较尴尬的就是,当初的设计并没有考虑到多组输出,因此可能出现一些未初始化的问题,所以导致测试数据后面几组答案错误。我采取的方法是,缩小变量的作用域,将变量尽可能定义在循环内部,这样可以有效避免初始化等多组数据出错的潜在可能(虽然根本方法是应该,保证做到每次自己清空的..,但是因为各个类之间的调用关系..有一些清空起来操作困难,而且代码会变长变长变长,最要命的是我只想到了写在主函数里面的方法..,如果把类给别人用,别人还是要自己清空= =...所以就不这样子做了...所以我选择了最简便快速的方法)
主要实现代码:
if (strcmp(argv[1],"-f") == 0) //"-f" 用文件输入输出
{
freopen(argv[2],"r",stdin);
freopen(argv[3],"w",stdout);
string str;
while (cin >> str)
{
//各种计算操作
}
}
2.新建Print类输出
这个好像也没啥好讲的...就是需要好多函数来处理各种各样的输出,比如输出表达式啦,计算结果啦,报错啦....(想函数名才是最费时间的...)
Print
类中的三个函数原型:
void PrErTpye(string ErrorType); //报错
void PrExpression(string Expression);//输出表达式(包括'=')
void PrResult(double Result); //输出结果
测试结果如下:
注:类似于:
0-3+(9-((10-8)+(9-86/2+3)-1010)(8-(1+8/4+2)))+5(1+(2+3)/5)/5/
这样非法的表达式(最后多出一个‘/’)依然没能处理
作业要求<2>
用图形的方式描述整个项目的框架,即各个类之间的调用关系。
- 框架图不等于流程图
- 调用关系用带箭头的线表示出来
- 能够体现出层次关系
我是上网找了一款在线绘制的网页[Draw.io]来制作框架图,然后保存.jpg格式
(并不太懂框架图怎么弄)
关于其他优化:
-
1.增加
CheckInput
类,将原本在Scan
类中的括号匹配的判断移到CheckInput
中,后续可以添加一些表达式判断方面的内容以及补全人为缩写像3(2+1)
这样类似的表达式等等. -
2.规范了类中
.h
与.cpp
中宏定义以及inlcude库头文件方面的规范(其实就是把.cpp
中的宏定义还有include
等移到.h
中 -
3.增加一些错误类型的区分
小结
(当我附上代码后来写小结发现,代码好长啊....)
本次作业难度其实不大,(其实是大一下事太多事太多,所以就一直拖一直拖,成功又是截止当天勉强完工)
主要就是熟悉一下文件输入输出方式,然后改进一下代码,整理一下思路,将其用框架图的形式完成,于是这次作业就做完了.本次作业没有遇到什么大问题。
最后附上代码
main.cpp
#include"Calculation.h"
#include"Scan.h"
#include"Print.h"
#include"CheckInput.h"
#include<iostream>
#include<cstdio>
#include<cstring>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
int main(int argc, char* argv[]) //用cmd传参的方法输入数据
{
if (strcmp(argv[1],"-f") == 0) //"-f" 用文件输入输出
{
freopen(argv[2],"r",stdin);
freopen(argv[3],"w",stdout);
string str;
while (cin >> str)
{
/*定义在循环内,缩小变量作用域,代替每次的初始化,避免造成多次输出留下问题 */
Scan get_scanf;
Print put_printf;
get_scanf.ToStringQueue(str);
CheckInput *ck = new CheckInput;
get_scanf.ErrorType += ck->CheckIfCorrect(get_scanf.que); //存入输入错误类型
if (get_scanf.ErrorType == "") //判断输入是否合法,无错误类型则合法
{
Calculation *cal = new Calculation;
cal->Calculate(get_scanf.que); //计算
put_printf.PrResult(cal->result);
delete cal;
}
else
{
put_printf.PrErTpye(get_scanf.ErrorType); //输出计算结果
}
str = "";
delete ck;
}
}
else
{
Scan get_scanf;
Print put_printf;
if (strcmp(argv[1],"-a") == 0)
{
put_printf.PrExpression(argv[2]);
get_scanf.ToStringQueue(argv[2]); //含有"-a"情况数据为第三个参数
}
else
{
get_scanf.ToStringQueue(argv[1]);
}
CheckInput *ck = new CheckInput;
get_scanf.ErrorType += ck->CheckIfCorrect(get_scanf.que);
if (get_scanf.ErrorType == "")
{
Calculation *cal = new Calculation;
cal->Calculate(get_scanf.que);
put_printf.PrResult(cal->result);
delete cal;
}
else
{
put_printf.PrErTpye(get_scanf.ErrorType);
}
delete ck;
}
}
JudgeRelaction.h
/************************************************************
FileName: JudgeRelaction.h
Author: 031502248
Date: 2016/4/4
Function:
配合Calculation类使用,用于分担其关于符号类型的判断
优先级问题等的处理
History:
<author> <time> <version> <desc>
***********************************************************/
#ifndef CHECKRELACTION_H
#define CHECKRELACTION_H
#include<string>
#include<cstring>
#include<map>
#include<iostream>
using namespace std;
class JudgeRelaction
{
public:
JudgeRelaction();
~JudgeRelaction();
bool JudIfChater(string ch);
char JudPrChater(string op1,string op2);
private:
char OprRelation[7][7];
map<string,int> p;
};
#endif
JudgeRelaction.cpp
#include "JudgeRelaction.h"
using namespace std;
JudgeRelaction::JudgeRelaction()
{
p["+"] = 0;
p["-"] = 1;
p["*"] = 2;
p["/"] = 3;
p["("] = 4;
p[")"] = 5;
p["#"] = 6;
//+-*/()# 竖op1,横op2
strcpy(OprRelation[0],">><<<>>");//'+'
strcpy(OprRelation[1],">><<<>>");//'-'
strcpy(OprRelation[2],">>>><>>");//'*'
strcpy(OprRelation[3],">>>><>>");//'/'
strcpy(OprRelation[4],"<<<<<= ");//'('
strcpy(OprRelation[5],">>>> >>");//')'
strcpy(OprRelation[6],"<<<<< =");//'#'
}
/*************************************************************
functionname: JudIfChater
Description: 判断是否为符号
Input: string ch:判断对象
Return: 返回是否为符号的结果
Others: NULL
**************************************************************/
bool JudgeRelaction::JudIfChater(string ch)
{
return ch == "+" || ch == "-" || ch == "*" || ch == "/"
|| ch == "(" || ch ==")" || ch == "#";
}
/*************************************************************
functionname: JudPrChater
Description: 判断两个符号的优先级
Input: string opr1:栈顶的符号
string opr2:队列传出的符号
Return: 返回优先级关系
Others: 1.默认优先级同级时,栈顶优先级高于队列
2.'('')'与'#''#'最终不参与运算,
所以用特殊优先级('=')另外考虑
**************************************************************/
char JudgeRelaction::JudPrChater(string opr1,string opr2)
{
return OprRelation[p[opr1]][p[opr2]];
}
JudgeRelaction::~JudgeRelaction()
{
}
Scan.h
/************************************************************
FileName: Scan.h
Author: 031502248
Date: 2016/2/16
Function:
处理输入数据,将其分为字符与数字
History:
<author> <time> <version> <desc>
031502248 16/3/23 2.0 修改代码规范
031502248 16/4/4-8 3.0 修复减号与负号问题的bug
031502248 16/5/2 4.0 增加错误类型的判断
***********************************************************/
#ifndef SCAN_H
#define SCAN_H
#include<iostream>
#include<queue>
#include<stack>
#include<cstring>
#include<string>
#define MAXDIGIT 10
using namespace std;
class Scan
{
public:
Scan();
~Scan();
void ToStringQueue(string input);
bool Judge(char temp);
queue<string> que;
string ErrorType;
private:
};
#endif
Scan.cpp
#include "Scan.h"
Scan::Scan()
{
ErrorType = "";
}
Scan::~Scan()
{
}
/*************************************************************
functionname: Judge
Description: 判断是否存在非法字符
Input: string input:输入字符
Return: 判断结果
Others: NULL
**************************************************************/
bool Scan::Judge(char temp)
{
if (temp >= '0' && temp <= '9' || temp == '.')
{
return true;
}
else if (temp == '+' || temp == '-' || temp == '*'
|| temp == '/' || temp == '(' || temp == ')' )
{
return true;
}
return false;
}
/*************************************************************
functionname: ToStringQueue
Description: 将输入的字符串,逐个的字符扫描这,
将数字和符号提取出来分别存入队列
Input: string input:输入字符串
Return: no return
Others: NULL
**************************************************************/
void Scan::ToStringQueue(string input)
{
string temp = ""; //默认为空串,用来存储数字和字符,传给队列
bool flag_digits = false; //判断是否存在数超过10位
bool flag_dot = false; //判断是否带小数点
bool flag_ch = false; //判断否存在非法字符
int n = input.size();
temp += input[0]; //直接先存储输入字符串的第一个字符
if (input[0] == '-' && input[1] == '(')
{
que.push("0"); //如果开头是-()形式,特殊处理为0-()的形式
}
if (!Judge(input[0])) //是否存在非法字符
{
flag_ch = true;
}
for (int i = 1; i < n; i++)
{
if (!Judge(input[i]))
{
flag_ch = true;
break;
}
/*当前字符是数字或小数点 */
if (input[i] >= '0' && input[i] <= '9' || input[i] == '.')
{
if (input[i] == '.')
{
flag_dot = true;
}
if ((input[i-1] >= '0' && input[i-1] <= '9') || input[i-1] == '.')
{
/*判断前一个字符是不是数字或者小数点,如果是,则累计存储,先不传给队列*/
temp += input[i];
}
else //前一个字符是符号,下面分情况讨论
{
if (input[i-1] == '+' || input[i-1] == '-')
{
if ((i-2<0 || input[i-2]<'0' || input[i-2] > '9') && input[i-2] != ')' )
{
temp += input[i];
}
else //如果符号前面是数字,那么该符号表示运算符
{
Scan::que.push(temp);
temp = "";
flag_dot = false;
temp += input[i];
}
}
else //前面符号不是+,-,可以把temp传到队列,更新coun
{
Scan::que.push(temp);
temp = "";
flag_dot = false;
temp += input[i];
}
}
}
else //如果当前字符是符号,将temp传入队列,更新coun;
{
Scan::que.push(temp);
temp = "";
flag_dot = false;
temp += input[i];
}
if (flag_ch || flag_digits)
{
break;
}
if (temp.size() > MAXDIGIT) //当判断当前temp数组中的数字是否超过10位
{
/*超过10位,判断是不是带符号,如果带符号,则要超过11位*/
if (temp[0] == '-' || temp[0] == '+')
{
if (temp.size() == MAXDIGIT + 2 && flag_dot == true) //有符号且有小数点要12位以上
{
}
else if(temp.size() > MAXDIGIT + 1) //无符号有小数点要11位以上
{
flag_digits == true;
break;
}
}
else
{
if (temp.size() == MAXDIGIT + 1 && flag_dot == true)
{
}
else
{
flag_digits = true;
break;
}
}
}
}
que.push(temp);
temp = "";
if (flag_digits || flag_ch) //存在超过10位小数,或者存在非法字符,清空队列
{
while (!que.empty())
{
que.pop();
}
if (flag_digits)
{
ErrorType += "over";
}
if (flag_ch)
{
ErrorType += "wrongch";
}
}
}
Calculation.h
/************************************************************
FileName: Calculation.h
Author: 031502248
Date: 2016/4/4
Function:
将用string类为存储方式的式子进行计算
History:
<author> <time> <version> <desc>
031502248 16/5/2 2.0 更改计算结果的输出方式
***********************************************************/
#ifndef CALCULATION_H
#define CALCULATION_H
#include "JudgeRelaction.h"
#include<iostream>
#include<cstring>
#include<stack>
#include<string>
#include<sstream>
#include<queue>
using namespace std;
class Calculation
{
public:
Calculation();
~Calculation();
void Calculate(queue<string> que);
double CalStrChStr(double Opr1,string ch,double Opr2);
double StrToDble(string temp);
double result;
private:
JudgeRelaction *ck;
};
#endif
Calculation.cpp
#include "Calculation.h"
#include "JudgeRelaction.h"
Calculation::Calculation()
{
ck = new JudgeRelaction;
}
Calculation::~Calculation()
{
delete ck;
}
/*************************************************************
functionname: StrToDble
Description: 将输入的string类转变为double类型
Input: string temp:输入字符串
Return: double data:string对应的double类型数据
Others: NULL
**************************************************************/
double Calculation::StrToDble(string temp)
{
stringstream stream;
stream.clear();
stream << temp;
double data;
stream >> data;
return data;
}
/*************************************************************
functionname: CalStrChStr
Description: 将输入的double类型与符号对应来进行计算
Input: double Opr1:第一个操作数
tring ch:符号
double Opr2:第二个操作数
Return: double ans:进行四则运算后的答案
Others: NULL
**************************************************************/
double Calculation::CalStrChStr(double Opr1,string ch,double Opr2)
{
double ans;
if (ch == "+")
{
ans = Opr1 + Opr2;
return ans;
}
else if (ch == "-")
{
ans = Opr1 - Opr2;
return ans;
}
else if (ch == "*")
{
ans = Opr1 * Opr2;
return ans;
}
else if (ch == "/")
{
ans = Opr1 / Opr2;
return ans;
}
}
/*************************************************************
functionname: Calculate
Description: 进行总的计算
Input: queue<string> que: 区分好的数字与字符
Return: NULL
Others: NULL
**************************************************************/
void Calculation::Calculate(queue<string> que)
{
stack<double> dig; //用于存储数字
stack<string> ch; //用于存储符号
ch.push("#"); //最初始栈低为"#",令'#'优先级最低,用来简化优先级的判断
que.push("#"); //末位为"#",用于结束循环
while (!ch.empty())
{
string temp = que.front();
que.pop();
if (ck->JudIfChater(temp) == true) //判断是否为符号
{
char priority = ck->JudPrChater(ch.top(),temp); //比较栈顶符号与temp中符号的优先级
while (priority == '>') //栈顶优先级高则出栈进行计算
{
double num2 = dig.top();
dig.pop();
double num1 = dig.top();
dig.pop();
double Num;
Num = CalStrChStr(num1,ch.top(),num2);
dig.push(Num); //将计算结果入数字栈
ch.pop();
priority = ck->JudPrChater(ch.top(),temp);
}
if (priority == '<') //优先级低入栈
{
ch.push(temp);
temp = "";
}
else if(priority == '=') //'('')'以及'#''#'优先级特殊考虑
{
ch.pop(); //"()"与"##"不参与运算,仅用于判断优先级,所以配对后直接出栈
temp = "";
}
}
else //当前是数字,直接入栈
{
double opr = StrToDble(temp);
dig.push(opr);
temp = "";
}
}
result = dig.top();
}
CheckInput.h
/************************************************************
FileName: CheckInput.h
Author: 031502248
Date: 2016/5/3
Function:
判断表达式是否合法以及补全部分手写情况下的表达式
History:
<author> <time> <version> <desc>
***********************************************************/
#ifndef CHECKINPUT_H
#define CHECKINPUT_H
#include<queue>
#include<string>
#include<stack>
using namespace std;
class CheckInput
{
public:
CheckInput();
~CheckInput();
string CheckIfCorrect(queue<string> que);
protected:
};
#endif
CheckInput.cpp
#include "CheckInput.h"
CheckInput::CheckInput()
{
}
CheckInput::~CheckInput()
{
}
/*************************************************************
functionname: CheckIfCorrect
Description: 判断是否出错
Input: queue<string> que:输入表需要判断的达式
Return: string : 输出错误类型
Others:
**************************************************************/
string CheckInput::CheckIfCorrect(queue<string> que)
{
string ErrorType = "";
bool flag_match = false; //判断括号匹配是否合法
stack<string> sta; //判断括号匹配需要使用到
queue<string> tmp = que;
while (!tmp.empty()) //判断括号匹配
{
string data = tmp.front();
tmp.pop();
if (data == "(")
{
sta.push("(");
}
else if (data == ")")
{
if (!sta.empty())
{
sta.pop();
}
else
{
flag_match = true;
break;
}
}
}
if (flag_match || !sta.empty())
{
ErrorType += "mismatch";
}
return ErrorType;
}
Print.h
/************************************************************
FileName: Print.h
Author: 031502248
Date: 2016/5/2
Function:
输出所有有关该项目需要输出的所有内容
History:
<author> <time> <version> <desc>
***********************************************************/
#ifndef PRINT_H
#define PRINT_H
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
class Print
{
public:
Print();
~Print();
void PrErTpye(string ErrorType);
void PrExpression(string Expression);
void PrResult(double Result);
protected:
};
#endif
Print.cpp
#include "Print.h"
Print::Print()
{
}
Print::~Print()
{
}
/*************************************************************
functionname: PrExpression
Description: 输出表达式
Input: string Expression:输入表达式
Return: no return
Others: PrExpression为printExpression的缩写
在保持可读性的情况下
**************************************************************/
void Print::PrExpression(string Expression)
{
cout << Expression << "= " ;
}
/*************************************************************
functionname: PrErTpye
Description: 输出错误类型
Input: string ErrorType:输入错误类型
Return: no return
Others: PrErType为printErrorType的缩写
**************************************************************/
void Print::PrErTpye(string ErrorType)
{
if (ErrorType == "over")
{
cout << "Error! over ten digits limit." << endl;
}
else if (ErrorType == "mismatch")
{
cout << "Error! Wrong expression." << endl;
}
else if (ErrorType == "wrongch")
{
cout << "Error! Exist illegal character." << endl;
}
else
{
cout << "Error!" << endl;
}
}
/*************************************************************
functionname: PrResult
Description: 输出计算结果
Input: double Result:计算结果
Return: no return
Others: PrResult为printResult的缩写
**************************************************************/
void Print::PrResult(double Result)
{
cout << Result << endl;
}