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; } } }