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


    这里是代码传送门Calculator4.0


    作业要求<1>

    以下表达中, 用X代表第一个参数,用Y代表第二个参数(如果有的话),用Z代表第三个参数(如果有的话)

    • 如果X为表达式,Y为空,那么只输出表达式的结果;
    • 如果X为“-a”,Y表达式。将表达式输出,后面接着表达式的结果(中间用一个空格隔开);
    • 如果X为“-f”,Y为txt文件的路径,则从该文件中读取表达式(每个表达式一行,有多行),并将答案输出到Z的路径上
      Y的路径例如:“D: est.txt”
      Z的路径例如:“D: esults.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;
    }
    
  • 相关阅读:
    LeetCode 1356. 根据数字二进制下1的数目排序
    Ubuntu LaTeX 中文环境配置 与 VSCode LaTeX Workshop
    LeetCode 57. 插入区间
    VSCode Ubuntu下调试失败 无法打开 libc-start.c raise.c等
    LeetCode 30. 串联所有单词的子串
    日期处理函数
    Stream 和 byte[] 之间的转换
    Math.Round {实现四舍五入的小技巧}
    重写alert弹窗
    js轮播图
  • 原文地址:https://www.cnblogs.com/Anani-leaf/p/5476152.html
Copyright © 2020-2023  润新知