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