第五次作业(计算器第三步)

这里是代码传送门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;
}
posted @ 2016-05-09 23:56  洛丶航  阅读(409)  评论(4编辑  收藏  举报