MOOC清华《程序设计基础》第9章:自动售卖程序 v 2.0

来源:互联网 发布:北京ios程序员工资待遇 编辑:程序博客网 时间:2024/04/28 23:13

txt配置文件:

5苹果=3.5香蕉=2.7梨子=2.3葡萄=3.3橘子=1.8

我对照视频手打并debug的源代码:(保留了前几版和测试的痕迹)

//可以一边分析一边设计一边写代码//先考虑整体框架,再考虑细节,称为“自顶向下,逐步求精” #include <iostream>#include <fstream>#include <cstdlib>#include <cmath>#include <iomanip>#include <cstring>using namespace std;struct Fruit_t{char name[20];double price;};bool GetFruits(Fruit_t* &fruits, int &fruit_number){ifstream fin("fruits.txt");if(!fin)  //如果txt文件损坏、打不开 return false;fin >> fruit_number;//test statements:测试证明文件是可以打开的,而且可以读入第一行的数字 //cout << fruit_number;fin >> ws;  //ws是whitespace的缩写,cin语句也可以这样用,跳过后面的回车空格制表符 fruits = new Fruit_t[fruit_number];for(int i = 0; i < fruit_number; i++){char buffer[100];  //buffer是缓冲区的意思 fin.getline(buffer, 100);char* p = strchr(buffer, '=');  //strchr()函数的功能是在字符串中查找一个字符 *p = '\0';strcpy(fruits[i].name, buffer);//test statements:测试结果发现,bug原因是txt配置文件出了问题!明明是5种水果,第一行却是数字6 //cout << fruits[i].name << endl;fruits[i].price = atof(p + 1);}fin.close();  //debug记录:出错的原因是这一行写到for循环体内部去了,千万注意 return true;}//注意GetFruits()函数一定要放到其他函数的前面来,否则文件的数据都没读出来,后面没法操作 void ShowMenu(){cout << "****************************************" << endl;cout << "* 欢迎使用自动售卖机,请输入您的选择。 *" << endl;cout << "* 1.  下订单                           *" << endl;cout << "* 2.  退出自动售卖                     *" << endl;cout << "****************************************" << endl;}void ShowSubMenu(Fruit_t* fruits, int fruit_number){cout << "++++++++++++++++++++++++++++++++++++++++" << endl;cout << "+ 开始处理订单,请输入您的选择。       +" << endl;for(int i = 0; i < fruit_number; i++){cout << "+" << setw(2) << i + 1 << ".  购买";cout << fruits[i].name << "(" << fixed << setw(5) << setprecision(2) << fruits[i].price << "元/公斤)";int len = strlen(fruits[i].name);for(int j = 0; j < 13 - len; j++)cout << ' ';cout << "+" << endl; }//cout << "+ x.  购买苹果(3.5元/公斤)           +" << endl;//cout << "+ x.  购买香蕉(2.7元/公斤)           +" << endl;cout << "+" << setw(2) << fruit_number + 1 << ".  结账                             +" << endl;cout << "+" << setw(2) << fruit_number + 2 << ".  放弃购买                         +" << endl;cout << "++++++++++++++++++++++++++++++++++++++++" << endl;}//苹果是北方水果,香蕉是南方水果,所以北京那边香蕉比苹果贵,但在武汉苹果比香蕉贵//子菜单的花边框用+组成,区别于主菜单的*边框 int GetInteger(){char buf[100] = {0};while(strlen(buf) == 0)  //用户直接输入回车 cin.getline(buf, 100);return atoi(buf);  //atoi函数是cstring头文件自带的 }double GetDouble(){char buf[100] = {0};while(strlen(buf) == 0)  //用户直接输入回车cin.getline(buf, 100);return atof(buf);  //atof函数也是cstring头文件自带的 }void DealOrder(Fruit_t* fruits, int fruit_number){//double apple_price = 3.5;//double apple_weight = 0;//double banana_price = 2.7;//double banana_weight = 0;double sum = 0;  //需严格注意变量的作用域 double* weight = new double[fruit_number];for(int i = 0; i < fruit_number; i++)weight[i] = 0;while(1){//显示子菜单 ShowSubMenu(fruits, fruit_number);//todo:显示已买水果总价cout << "已购买水果总价: " << fixed << setprecision(2) << setw(8) << sum << "元" << endl; cout << "您的选择是:";int input;//cin >> input;input = GetInteger();if(input > 0 && input <= fruit_number){cout << "请输入称重(公斤):";double w = GetDouble();sum += fruits[input - 1].price * w;weight[input - 1] += w;}if(input == fruit_number + 1){if(sum > 1E-6)  //确实买了水果 {cout << "您一共购买了";for(int i = 0; i < fruit_number; i++)if(fabs(weight[i]) > 1E-6)cout << weight[i] << "公斤" << fruits[i].name << ",";cout << "总价是" << sum << "元" << endl;system("pause");}break;}if(input == fruit_number + 2)break;/*switch(input){case 1://todo:处理买苹果cout << "请输入称重(公斤):";//cin >> weight;weight = GetDouble();sum += apple_price * weight; //注意分清weight和apple_weight apple_weight += weight;break;case 2://todo:处理买香蕉cout << "请输入称重(公斤):";//cin >> weight;weight = GetDouble();sum += banana_price * weight;banana_weight += weight;break;case 3://todo:显示总价//cout << "已购买水果总价:" << fixed << setprecision(2) << setw(8) << sum << "元" << endl;if(sum > 1E-6){cout << "您一共购买了";if(fabs(apple_weight) > 1E-6)  //加绝对值的原因是允许“买了苹果后嫌买多了又退几个”的情况 cout << apple_weight << "公斤苹果,";if(fabs(banana_weight) > 1E-6)  //加绝对值原因同上,退货的时候也要称重,所以存在负数,总重可能略小于0 cout << banana_weight << "公斤香蕉,";cout << "总价是" << sum << "元" << endl;//核心技巧:前面已经对sum作了输出流格式控制,然后所有跟sum发生计算关系的变量//都统一成了与sum相同的输出流格式,所以这里就不必再加格式控制符了 system("pause");}case 4:system("cls");return; }*/}delete []weight;system("cls");}int main(){Fruit_t* fruits = NULL;int fruit_number;//test statements:不加这一句也行 //GetFruits(fruits, fruit_number);if(!GetFruits(fruits, fruit_number)){cout << "配置文件错误!" << endl;return 0;}//测试代码:测试结果显示文件并没有完全读入结构体数组,说明GetFruits()函数有问题 //测试GetFruit()后发现:bug原因是txt配置文件出了问题!明明是5种水果,第一行却是数字6 //修改txt配置文件后,一切正常!所以:txt配置文件的容错性很低,有待修改! //for(int i = 0; i < fruit_number; i++)//cout << fruits[i].name << '=' << fruits[i].price << endl; while(1){ShowMenu();cout << "您的选择是:";int input;//cin >> input;input = GetInteger();switch(input){case 1:DealOrder(fruits, fruit_number);break;case 2:delete []fruits;return 0;}}}

经过删除多余注释和测试代码后的最终源代码:

//可以一边分析一边设计一边写代码//先考虑整体框架,再考虑细节,称为“自顶向下,逐步求精” #include <iostream>#include <fstream>#include <cstdlib>#include <cmath>#include <iomanip>#include <cstring>using namespace std;struct Fruit_t{char name[20];double price;};bool GetFruits(Fruit_t* &fruits, int &fruit_number){ifstream fin("fruits.txt");if(!fin)  //如果txt文件损坏、打不开 return false;fin >> fruit_number;fin >> ws;  //ws是whitespace的缩写,cin语句也可以这样用,跳过后面的回车空格制表符 fruits = new Fruit_t[fruit_number];for(int i = 0; i < fruit_number; i++){char buffer[100];  //buffer是缓冲区的意思 fin.getline(buffer, 100);char* p = strchr(buffer, '=');  //strchr()函数的功能是在字符串中查找一个字符 *p = '\0';strcpy(fruits[i].name, buffer);fruits[i].price = atof(p + 1);}fin.close();  //debug记录:出错的原因是这一行写到for循环体内部去了,千万注意 return true;}//注意GetFruits()函数一定要放到其他函数的前面来,否则文件的数据都没读出来,后面没法操作 void ShowMenu(){cout << "****************************************" << endl;cout << "* 欢迎使用自动售卖机,请输入您的选择。 *" << endl;cout << "* 1.  下订单                           *" << endl;cout << "* 2.  退出自动售卖                     *" << endl;cout << "****************************************" << endl;}void ShowSubMenu(Fruit_t* fruits, int fruit_number){cout << "++++++++++++++++++++++++++++++++++++++++" << endl;cout << "+ 开始处理订单,请输入您的选择。       +" << endl;for(int i = 0; i < fruit_number; i++){cout << "+" << setw(2) << i + 1 << ".  购买";cout << fruits[i].name << "(" << fixed << setw(5) << setprecision(2) << fruits[i].price << "元/公斤)";int len = strlen(fruits[i].name);for(int j = 0; j < 13 - len; j++)cout << ' ';cout << "+" << endl; }cout << "+" << setw(2) << fruit_number + 1 << ".  结账                             +" << endl;cout << "+" << setw(2) << fruit_number + 2 << ".  放弃购买                         +" << endl;cout << "++++++++++++++++++++++++++++++++++++++++" << endl;}//苹果是北方水果,香蕉是南方水果,所以北京那边香蕉比苹果贵,但在武汉苹果比香蕉贵//子菜单的花边框用+组成,区别于主菜单的*边框 int GetInteger(){char buf[100] = {0};while(strlen(buf) == 0)  //用户直接输入回车 cin.getline(buf, 100);return atoi(buf);  //atoi函数是cstring头文件自带的 }double GetDouble(){char buf[100] = {0};while(strlen(buf) == 0)  //用户直接输入回车cin.getline(buf, 100);return atof(buf);  //atof函数也是cstring头文件自带的 }void DealOrder(Fruit_t* fruits, int fruit_number){double sum = 0;  //需严格注意变量的作用域 double* weight = new double[fruit_number];for(int i = 0; i < fruit_number; i++)weight[i] = 0;while(1){//显示子菜单 ShowSubMenu(fruits, fruit_number);//todo:显示已买水果总价cout << "已购买水果总价: " << fixed << setprecision(2) << setw(8) << sum << "元" << endl; cout << "您的选择是:";int input;input = GetInteger();if(input > 0 && input <= fruit_number){cout << "请输入称重(公斤):";double w = GetDouble();sum += fruits[input - 1].price * w;weight[input - 1] += w;}if(input == fruit_number + 1){if(sum > 1E-6)  //确实买了水果 {cout << "您一共购买了";for(int i = 0; i < fruit_number; i++)if(fabs(weight[i]) > 1E-6)cout << weight[i] << "公斤" << fruits[i].name << ",";cout << "总价是" << sum << "元" << endl;system("pause");}break;}if(input == fruit_number + 2)break;}delete []weight;system("cls");}int main(){Fruit_t* fruits = NULL;int fruit_number;if(!GetFruits(fruits, fruit_number)){cout << "配置文件错误!" << endl;return 0;}while(1){ShowMenu();cout << "您的选择是:";int input;//cin >> input;input = GetInteger();switch(input){case 1:DealOrder(fruits, fruit_number);break;case 2:delete []fruits;return 0;}}}

阅读全文
0 0