Vc++课程设计报告
题目名称:四则运算 班级:测控技术与仪器 姓名:周赛
学号:914101170150 一、程序功能简介
该程序用字符串形式接受一个带括号的四则运算表达式,然后按照四则运算优先级的算法先括号,后乘方、乘除、加减这样的顺序将这个算式解出。
该程序的设计比较巧妙,是在主程序中反复查找最后一对括号,将其中的结果计算出来,去掉括号,这样的过程持续下去,最后导致所有的括号都去除,解出算式。 二、课程设计要求
(1)用类的要求改写程序,将数据和函数封装到类中。
(2)修改主程序,使程序可以反复运算,直到选择退出为止。 (3)扩充程序功能,使程序适合浮点数运算。
(4)增加程序的判断功能,当有非法的输入时(如字母等),给出提示信息并退出运算,当输入带有空格时,能将空格滤除。 三、程序设计思想 (1)类的封装
实际上该字符串类基本类似于c++中的string类,比string类多定义了浮点数与字符串的相互转换的函数,输入输出友元函数,可以从string类派生出CStr类,这样,类的定义就更加简单,且可以利用很多现成的成员函数。 (2)主程序结构算法的设置
定义char类型的变量用来存储字符串,定义CStr类用于对字符串操作函数的引用。 先对输入字符串用Judge函数进行输入正确性的判断及取出空格内容; 然后用charinstr函数在字符串中反复查找最后一对括号; 用midstr函数提取出来,用calculate函数进行计算;
并用left函数与right函数及addstrings函数将所找到这对括号的左边、右边及运算结果以字符串的形式合并成一个新的字符串; 重复上述操作直至字符串中不再有括号;
最后用calculate函数进行最后的计算返回计算结果。
紧接着进行是否退出计算的输入判断,完成程序反复运算的功能。 四、详细设计
①判断功能:用类中定义的judge函数实现,返回值为1即合法,为0即不合法。
去除空格:定义p、s两个指针指向字符串,r指针指向p指针实现p、s指针在字符串中的遍历,用p指针找到第一处空格处,再用s指针找到p指向位置以后最近一个不是空格处,赋值语句*p=*s,继续向下遍历字符串直至结束。
非法输入:定义destination指向字符串,列举算式中出现的合法的字符,通过遍历字符串找到非法输入字符,返回0;
非法算式逻辑:包括运算符号相连,以及违背运算法则等现象,通过对运算符号的遍历比较实现;还有包括左括号前或右括号后紧挨数字,通过定义一个新字符串数组指向字符串来寻找括号并判断括号前后是否合法;非法,返回0; 全都合法返回1.
②数字字符串转换成数字:用类中定义的val函数实现,扩充到浮点数的运算范围。
定义char类型的source指针指向字符串,用charinstr函数寻找字符串中小数点位置,以此判断整数还是浮点数,如果整数即用10的相应位数的乘方来乘以相应字符位置字符的数字,并进行累加,最后返回result;如果是浮点数,则分为整数与小数部分依次进行计算,整数同上,小数部分则用相应字符位置字符所代表的数字除以10的相应位数的乘方,并进行累加,最后将整数小数部分的和返回result。
③字符串的连接,通过类中定义的left、right、midstr、addstrings函数配合实现。 通过left、right、midstr三个函数进行字符串的提取,addstrtings函数进行三个指针的遍历实现两个字符串的合并。
④数字的字符串化,通过类中定义的str函数实现。
类比val函数一样区分整数与浮点数,将数字的每一个数字单个提取出来通过与字符‘0’的比较转化为相应字符复制给递增的指针。 ⑤算式的计算,通过类中定义的calculate函数实现。
依次按照^*/+-的顺序用charinstr函数寻找运算符号,并用相应函数进行字符串与数字之间的转换,以及新的字符串的合并,返回计算结果。
⑥友元函数的定义,定义输入输出友元函数,重载赋值运算符等,实现字符串之间的加法。 五、程序调试的过程 出现问题:
①开始编写完整个程序,出现许多编写错误,一个个改正;
②在实现浮点数的扩充过程中的小数部分,以及输入合法性的判断过程漏掉情
况,指针的误用等;
③有时单独的一个函数不能搞懂或错误不能改正,就单独在编写一个类似程序,
多次调试运行,来弄懂并加以改正;
解决办法:查阅书籍,上网搜索,独立思考,询问同学老师,多次上机调试等; 六、输入输出数据 开始界面:
非法输入判断:
①非法字母:②运算符紧挨:
③左括号前或右括号后为数字:
④括号为中文符号:
清理空格:
运算第一个输入字符串算式结果:
退出判断:继续运算:
退出运算:
浮点数的加入:
七、课程设计总结
经过这次庞大复杂的课程设计过程,是我对c++知识有了更加深刻的理解,引起了我对c++语言世界的无限好奇心和探索欲,感慨到了世界的神奇与微妙,程序控制的无限用途以及自身的渺小;
在其中编写判断括号前后是否合法语句时,指针遍历不易实现,于是经过多次调试后重新定义了一个字符串数组来实现,这给了我很大的启示,只要努力并不断尝试新的东西
一定会成功;
还有程序中友元函数与运算符重载部分,由于知识掌握不够牢固,反复调试查阅书
籍,最终得到了解决,也使得我对这方面的知识获得了更为深刻的理解与认知;
浮点数的加入要进行小数点数位的取舍,函数中设定为小数点儿后六位,因此浮点
数运算过程中每次都会出现六位小数的现象,使得结果有0.000001的误差,通过单独编写类似函数多次调试,使得我对c++语言中对double型向int型变量转化的规律的理解获得了更深层次的理解;
通过编写过程,体会到了细节的厉害,有时一个微小的错误就能使整个程序而不能
正确运行; 八、源程序注解
#include
char pause;
//算出字符串的长素
int len(char*source) { int retval=0; }
class CStr { private: };
void CStr::left(CStr & str1,int length) 字符串
//从参数指向的地址左边取Length个字符赋给目的
int nlen; //字符串长度 char *pstr; //字符串首地址 CStr(){};
CStr(char *str) {nlen=len(str);pstr=str;} int Getlen(){return nlen;} char*Getstr(){return pstr;} char*Getpstr(){return pstr;}
void midstr(CStr & str1,int start,int length); void left(CStr & str1,int length);
/*返回指定字符串类中从nStart序号开
//返回字符串长度 //返回字符串首地址
//拷贝构造函数
//定义字符串类
while(*(source+retval++)!=0){} return --retval;
public:
CStr(CStr&str){nlen=str.Getlen();pstr=str.Getstr();}
始nLength长度的字符*/
/*返回指定字符串中从左边开始nLength个字符*/ /*返回指定字符串中从右边开始nLength个字符*/
void right(CStr & str1,int length); friend int charinstr(char); double val(); str(double val);
calculate(); //计算该字符串所代表的四则运算的值(内无括号)
//友元函数,判断字符是否在字符串中
//求字符串代表的数字字符的数值
//将数值表示成字符串的形式
//重载输入运算符
friend istream &operator>>(istream &,CStr &); int Judge(); //判断输入是否合法,滤除空格
{ }
char*destination=pstr; char *source=str1.Getstr(); *(destination+--length+1)=0;
//目的字符串以0 结尾
while(length>=0) //赋值Length个字符
{ *(destination+length)=*(source+length--); }
//从参数指向的地址处从start位
void CStr::midstr(CStr & str1,int start,int length) 置开始取length个字符赋给目的字符串 { }
void CStr::right(CStr & str1,int length) 字符串 { }
int charinstr( char *destination,char char_to_find) 目的字符串中,若在,返回其所在位置,若不再返回0 { int pos=0; }
CStr::str(double value) { char*tempdest=pstr; int a=0; int b=(int)value;
double c=value-b;
int multiplier=1000000000; while(*(destination+pos)!=0) }
return 0;
{ return pos; }
//循环查找
{ if(char_to_find==*(destination+pos++))
//找到,返回其位置
char *source=str1.Getstr(); while(*source!=0){ source++; } char*destination=pstr; source-=length;
//跳至欲赋值的首地址
*(destination+--length+1)=0; char *source=str1.Getstr();
source+=start-1; //开始赋值处的地址 *(destination+--length+1)=0; {
char*destination=pstr;
//目的字符串以0 结尾
while(length>=0) //赋值Length个字符
*(destination+length)=*(source+length--); }
//从参数指向的地址右边取Length个字符赋给目的
//移至字符串尾部
//目的字符串以0 结尾
while(length>=0) //赋值Length个字符
{ *(destination+length)=*(source+length--); }
//判断字符串char_to_find是否在
//将value的值转换为字符串的形式返回,加入浮点数
for(multiplier=1000000000;multiplier!=0;multiplier/=10) {
*tempdest='0'+(char)(b/multiplier); b-=(b/multiplier)*multiplier; if((*tempdest!='0')||(a))
{
a++;tempdest++; }
}
}
if(c==0) //如果value为整数 {*tempdest=0;}
else //如果value为浮点数 { }
//两个字符串合并,结果在第
*tempdest++='.'; for(a=1;a<=6;a++) { }
*tempdest=0; //赋值字符串最后一位为0
c*=10;int d=(int)c; if(d!=0&&a<=6) {*tempdest++='0'+d;} c-=d;
char*addstrings(char*destination,char*source1,char*source2) 一个参数中
{ char*tempdest=destination;
while(*source1!=0){*(tempdest++)=*(source1++);} while(*source2!=0){*(tempdest++)=*(source2++);} }
double pwr(double a,double b) {
double result=1;
//计算a的b次方
*tempdest=0; //NULL return destination;
for(int c=1;c<=b;c++){result*=a;} return result; }
double CStr::val() {
char*source=pstr; double result=0;
//将数字字符串转换成数值(包括浮点数)
CStr nstr(source);
int z=charinstr(source,'.'); //从source字符串中找到小数点
if(z==0) //如果为整数 { }
else //如果为小数,此时指针指向小数点的位置 {
char a1[50]; A1.left(nstr,z-1);
//返回此时nstr指向的地址左边的z-1个字符,即所需浮点数的整
CStr A1(a1);
int multiplier=(int)pwr(10,len(source)-1); while(*source!=0) {
//依次遍历每一个字符
result+=(*(source++)-'0')*multiplier;multiplier/=10;} //将相应字符转化为对应
位数的整数,并依次累加获得所需整数数
return result;
数部分
}
char*nint=A1.Getstr(); while(*nint!=0) {
//将A1的内容赋值给nint指针
int multiplier=(int)pwr(10,len(nint)-1); //定义multiplier等于10的整数位数的乘方
result+=(double)(*(nint++)-'0')*multiplier;multiplier/=10; }
//将浮点数整
数部分字符串转换为数值
char a3[50];
//返回从nstr指向地址的z+1位置开始的
CStr A3(a3);
A3.midstr(nstr,z+1,len(source)-z); }
return result;
char *ndouble=A3.Getstr();
(len(source)-z)个字符,即所需浮点数的小数部分
//将A3的内容赋值给ndouble指针
//依次遍历ndouble指向的每一个字符
for(multiplier=10;*ndouble!=0;ndouble++)
{ result+=((double)(*(ndouble)-'0'))/(double)multiplier; multiplier*=10; } //将相应字符除以对应数位的10的乘方获得该位的小数并依次相加
char*assignstr(char*source,char*destination)//字符串赋值 {
char*tempdest=destination;
while (source!=0){*(tempdest++)=*(source++);}
*tempdest=0; return destination; }
CStr::calculate()
//计算数字算式形式的字符串,将其转换为数字形式并计算出来,然后再将结果转换为字符串的形式,此算式没有括号,遵循的原则是先乘除后加减 {
char*string=pstr;
char buf1[50],buf2[50],buf3[50],buf4[50],buf5[50]; char opstr[6]=\double leftnr; double rightnr; int oppos; int z;
double result;
for(int pos_in_opstr=0;pos_in_opstr<=4;pos_in_opstr++) { while(charinstr(string,opstr[pos_in_opstr]))
{ oppos=charinstr(string,opstr[pos_in_opstr]); //找到后,将运算符左边的数字字符
for (z=oppos-2;z>=0;z--) {
if
//先找优先级高的运算符,直
CStr nstr(string);
CStr cuf1(buf1),cuf2(buf2),cuf3(buf3),cuf4(buf4),cuf5(buf5);
至将算式中所有的运算符都找出来
取出转换为数字
((*(string+z)=='+')||(*(string+z)=='/')||(*(string+z)=='-')||(*(string+z)=='*')||(*(string+z)=='^'))
}
{ cuf1.midstr(nstr,z+2,oppos-z-2); leftnr=cuf1.val();
z=-1; }
else if(z==0) {
cuf1.left(nstr,oppos-1); leftnr=cuf1.val(); }
//再将运算符右边的数字字符取出转换为数字
for(z=oppos;z if((*(string+z)=='+')||(*(string+z)=='/')||(*(string+z)=='-')||(*(string+z)=='*')||(*(string+z)=='^')) { cuf2.midstr(nstr,oppos+1,z-oppos); rightnr=cuf2.val();z=len(string);} else if(z==len(string)-1) { cuf2.right(nstr,len(string)-oppos); rightnr=cuf2.val(); } //对这两个数字进行计算,结果在result中 if(opstr[pos_in_opstr]=='+') {result=leftnr+rightnr;} else if (opstr[pos_in_opstr]=='-') {result=leftnr-rightnr;} else if (opstr[pos_in_opstr]=='/') {result=leftnr/rightnr;} else if (opstr[pos_in_opstr]=='*') {result=leftnr*rightnr;} else if (opstr[pos_in_opstr]=='^') {result=pwr(leftnr,rightnr);} //计算后,将结果转换为字符串,然后将左右未运算过的字符串与其连接起来 cuf4.left(nstr,oppos-len(&buf1[0])-1); cuf5.str(result); addstrings(&buf3[0],cuf4.pstr,cuf5.pstr); cuf5.right(nstr,len(string)-oppos-len(&buf2[0])); addstrings(string,cuf3.pstr,cuf5.pstr); } } cout<<\运行的结果是\ //输出这个字符 } istream & operator>>(istream&is,CStr&ss) { char a[50]; char *x=a; char *y=ss.pstr; cin.getline(x,50); while(*x!=0) {*(y++)=*(x++);} *y=0; return is; } } int CStr::Judge() //新增程序判断功能 {//三个指针依次遍历字符串,去除空格 //找到除基本运算符和数字外的非法输入 char *destination=source; while(*destination!=0) { if('('<=*destination&&*destination<='+'||'-'<=*destination&&*destination<='/'||'0'<=*d } //找到非法逻辑 destination=pstr; if(*destination=='^'||*destination=='*'||*destination=='/'||*destination=='+')//一开始 { return 0;} {destination++;} //找到非法输入,返回0 char *p,*s,*r,*source=pstr; p=source;s=p+1; while(*p!=0) { } r=p; if(*p==' ') { while(*s!=0) {*p=*s;p++;s++;} } *p=0;p=r;s=p+1; continue; p=r+1;s=p+1; estination&&*destination<='9'||*destination=='^'||*destination==' ') else{return 0;} 为运算符 int pos_in_opstr; int pos;int z; char opstr[6]=\ for(pos_in_opstr=0;pos_in_opstr<=4;pos_in_opstr++) { while(charinstr(source,opstr[pos_in_opstr])) if(*(source+z)=='^'||*(source+z)=='*'||*(source+z)=='/'||*(source+z)=='+'||*(source+z) if(*(source+z)=='^'||*(source+z)=='*'||*(source+z)=='/'||*(source+z)=='+'||*(source+z) {return 0;} //两个运算符相距非法,返回0 {return 0;} //运算符后紧接着又是运算符,返回0 { pos=charinstr(source,opstr[pos_in_opstr]); z=pos; =='-'||*(source+z)==0) z-=2; =='-'||*(source+z)==0) } } } else{source+=pos;} //继续遍历字符串 char m[50];int n=0; strcpy(m,pstr); while(m[n]) { if(m[n]=='(') } return 1; cout<<\去除空格后的算式:\ { } if(m[n]==')') { } n++; if(m[n+1]==0) {n++;continue;} {return 0;break;} //右括号位于结尾,继续 else if('0'<=m[n+1]&&m[n+1]<='9') //如果右括号右边是数字,没有运算符号,非法,返回0 if(n==0) {n++;continue;} {return 0;break;} //左括号位于开头,继续向下遍历 //如果左括号左边是数字,没有运算符号,非法,返回0 else if('0'<=m[n-1]&&m[n-1]<='9') void main() { CStr myrecord; cout<<\cout<<\cout<<\cout<<\cin.get(pause); system(\int choice=1; //进行退出判断,使程序可以反复运算 cout<<\欢迎使用四则运算程序 \\2 \\n\ cout<<\ cout<<\请按回车键继续 \\2 \\n\ while(choice) { char strn[50],duf1[50],duf2[50],duf3[50],duf4[50],duf5[50]; CStr buf1(duf1),buf2(duf2),buf3(duf3),buf4(duf4),buf5(duf5),origin(strn),oristr(strn); int z,lastopen; cout<<\请输入一个算式\\n\operator >>(cin,oristr); if(oristr.Judge()==0) else { cout<<\输入的算式是:\ while(charinstr(&strn[0],'(')) //判断左括号 //输出去除空格后的算式字符串 //判断输入是否合法 {; cout<<\输入有误,请重新输入\\n\ } } { for(z=0;z<=len(&strn[0]);z++) } //去掉括号后,计算最后剩余的算式 { if(strn[z]=='(') {lastopen=z;} //找到最后一个左括号的位置 } } cout<<\新的式子是:\z=len(&strn[0])+1; if(strn[z]==')') //找到第一个右括号,将左右括号中的字符串取出计算 { buf1.midstr(oristr,lastopen+2,z-lastopen-1); cout<<\在\中,找到\提出进buf3.left(oristr,lastopen); buf2.right(oristr,len(&strn[0])-z-1); buf1.calculate(); 行计算\ //然后将计算后的结果与原先左右括号两边的字符串合并,相当于去掉一对括号 addstrings(&strn[0],addstrings(&duf4[0],buf3.Getstr(),buf1.Getstr()),buf2.Getstr()); oristr.calculate(); cout<<\计算结果为:\ cout<<\退出吗?\\n\继续计算请输入1,退出程序请输入0\cin>>choice; cin.get(); cout<<\现在退出运算!\\n\} 九、程序不足:浮点数的误差问题,上文中提及过;以及不能进行负数运算,曾试图对课本源程序进行此类改进,但最终无果。