文章目录
- 前言
- 1.基础支持
- 2. 几个小坑
- 2.1 为什么要用引用传值
- 2.2 头文件的作用
- 2.3 while true的使用和跳出
- 3. 未解决的问题
前言
是尚硅谷的C++第一季的项目,我跟着敲下来了,发现几个坑点,记录下来;
1.基础支持
有这个则只require once
#pragram once
关键数据结构
Struct AccountItem{string itemType;int amount;string detail;
}
关键方法:C++ 读取文件的四种方式
ref:https://blog.csdn.net/weixin_62210536/article/details/127248352
ifstream input(FILENAME); 是基于运算符自身的性质,会将一切空格当作终止条件,这样的话就无法输出空格信息,采用这种方法会将一行信息进行分段读出,对于类对象的信息读取是很有用的。
// 读取文件
void loadDataFromFile(vector<AccountItem>& items){ifstream input(FILENAME);// 遇见空格 /t 等就断开AccountItem item;while (input >> item.itemType && input >> item.amount && input >> item.detail){items.push_back(item);}input.close();
}
// 写入文件
// 讲一条账目写入文件
void insertItemIntoFile(AccountItem item){// 如果没有文件,那么生成空文件;如果有文件,那么在文件尾追加。ofstream output(FILENAME, ios::out | ios::app);output << item.itemType << "\t" << item.amount << "\t" << item.detail << endl;output.close();
}
ifstream 和ofstream 是两个标准库;
读入数据:返回int
// 读取键盘输入的金额数
int readAmount()
{int amount;string str;while(true){getline(cin, str);try{amount = stoi(str);break;}catch(invalid_argument e){cout << "输入错误,请正确输入数字:";}}return amount;
}
读入数据:返回char
char readQuitConfirm(){string str;while (true){getline(cin, str);if(str.size()!=1||toupper(str[0])!='Y' && toupper(str[0]) != 'N')cout << "请输入 Y 或者 N";elsebreak; }return toupper(str[0]);
}
2. 几个小坑
2.1 为什么要用引用传值
// ------------1. 记账操作------------//
void accounting(vector<AccountItem> &items){switch (readMenuSelection(3)){case '1':income(items);break;case '2':expand(items);break;default:break;}
}
所有的AccountItem的Vector结构体都要引用传值,是因为在输出记账record的时候,没有读取文件,而是直接变量的vector实现的。
2.2 头文件的作用
C++ 只能引入头文件(.h文件),而不能引用.cpp文件,所以.cpp 文件只有一个。
g++ demo.cpp xx1.cpp xx2.cpp xx3.cpp -o demo
会生成一个 demo.exe 的文件 头文件在demo.cpp 中包含并定义,N多方法在 xx1.cpp, xx2.cpp, xx3.cpp中实现。这一点坑了我很长时间。
另外一个方式可以 把函数直接定义和实现在 common.h account_item.h 中。
2.3 while true的使用和跳出
这个很难总结,自己体会把。
···
cin>>str;
getline(cin, str);
的区别, 第一个 不带 空格,断行
第二个带空格断行,
···
会给下一个等待键盘输入的方法,放入 \n, 很不友好,建议使用 getline(cin, str)
这个函数原来用的第一种方式,造成 item.detail 收到了 断行
3. 未解决的问题
我用vscode 编码的, 写入的数据 中午是乱码,但是piple走通了。
git地址:https://github.com/justinge/cpp_xiaogu
全体代码也贴一下:
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
#define FILENAME "./account_items.txt"
#define INCOME "收入"
#define EXPAND "支出"
using namespace std;
struct AccountItem{string itemType;int amount;string detail;
};
void showMainMenu();
char readMenuSelection(int options);
char readQuitConfirm();
void showAccountingMenu();
void showQueryMenu();
void income(vector<AccountItem> &items);
void expand(vector<AccountItem> &items);
void accounting(vector<AccountItem> &items);
void query(vector<AccountItem> &items);
void loadDataFromFile(vector<AccountItem> &items);
int main(){cout << "justin 盖建明" << endl;vector<AccountItem> items;loadDataFromFile(items);bool quit = false;while(!quit){showMainMenu();char key = readMenuSelection(3);switch (key){case '1': // 1.记账/* code */showAccountingMenu();accounting(items);break;case '2': // 查看账单showQueryMenu();query(items);break;case '3':cout << "\n确认要退出:Y是,N不是(Y/N): ";if (readQuitConfirm()=='Y'){quit = true;}break;default:break;}}return 1;
}void showMainMenu(){cout << "主菜单" <<endl;cout << "1. 记账" <<endl;cout << "2. 查看" << endl;cout << "3. 结束" << endl;cout << "请输入所需要的菜单!(1-3)" <<endl;
}
void showQueryMenu()
{ cout << "-------------------------------------------------------" << endl;cout << "|=============== 选择查询条件 ====================|" << endl;cout << "| |" << endl;cout << "|*************** 1 统计所有账目 ********************|" << endl;cout << "|*************** 2 统 计 收 入 ********************|" << endl;cout << "|*************** 3 统 计 支 出 ********************|" << endl;cout << "|*************** 4 返回主菜单 ********************|" << endl;cout << "|_____________________________________________________|" << endl;cout << "\n请选择(1 - 4):";
}
char readMenuSelection(int options){string str;while(true){getline(cin,str);if (str.size() !=1 ||str[0] - '0' <= 0 || str[0] - '0' > options){cout << "请输入所需要的菜单错误!";}else{break;}}return str[0];
}
char readQuitConfirm(){string str;while(true){cin>>str;if (str.size()!=1 || toupper((str[0]) != 'Y' && toupper(str[0])!= 'N')){cout << "请输入:Y or N!" << endl;}else{break;}}return str[0];
}void showAccountingMenu(){cout << "-------------------------------------------------------" << endl;cout << "|=============== 选择记账种类 ====================|" << endl;cout << "| |" << endl;cout << "|*************** 1 收 入 ********************|" << endl;cout << "|*************** 2 支 出 ********************|" << endl;cout << "|*************** 3 返回主菜单 ********************|" << endl;cout << "|_____________________________________________________|" << endl;cout << "\n请选择(1 - 3):";
}
void accounting(vector<AccountItem> &items){char key = readMenuSelection(3);switch (key){case '1':income(items);break;case '2':expand(items);break;default:break;}
}
void insertItemIntoFile(AccountItem item){ofstream output(FILENAME, ios::out | ios::app);output << item.itemType << "\t" << item.amount << "\t" << item.detail << endl;output.close();
}
int readAmount(){string str;int amount;while(true){getline(cin, str);try{amount = stoi(str);break;}catch(invalid_argument e){cout << "输入错误,请正确输入数字:" << endl;}}return amount;
}
void income(vector<AccountItem> &items){AccountItem item;item.itemType = INCOME;cout << "请输入金额:";item.amount = readAmount();cout << "\n备注:";getline(cin,item.detail);items.push_back(item);insertItemIntoFile(item);// 显示成功信息cout << "\n------------------------记账成功!--------------------------\n" << endl;cout << "\n请按回车键返回主菜单..." << endl;string line;getline(cin, line);
}void expand(vector<AccountItem> &items){AccountItem item;item.itemType = EXPAND;cout << "请输入金额:" << endl;item.amount = -readAmount();cout << "\n备注:" << endl;getline(cin,item.detail);items.push_back(item);insertItemIntoFile(item);// 显示成功信息cout << "\n------------------------记账成功!--------------------------\n" << endl;
}
void printItems(AccountItem item){cout << item.itemType << "\t\t" << item.amount << "\t\t" << item.detail << endl;
}
void queryItems(vector<AccountItem> items){cout << "---------------- 查询结果 ---------------------" << endl;cout << "\n类型\t\t金额\t\t备注\n" << endl;int total = 0;for (AccountItem item : items){total+=item.amount;printItems(item);}// 输出信息cout << "===================================================\n" << endl;cout << "总收支:" << total << endl;
}void queryItems(vector<AccountItem> items,string itemType){cout << "---------------- 查询结果 ---------------------" << endl;cout << "\n类型\t\t金额\t\t备注\n" << endl;int total = 0;for (AccountItem item : items){if (item.itemType!=itemType)continue;total+=item.amount;printItems(item);}// 输出信息cout << "===================================================\n" << endl;cout << ((itemType == INCOME) ? "总收入:":"总支出:") << total << endl;
}void query(vector<AccountItem> &items){char key = readMenuSelection(4);switch (key){case '1':queryItems(items);break;case '2':queryItems(items,INCOME);break;case '3':queryItems(items,EXPAND);default:break;}cout << "\n请按回车键返回主菜单..." << endl;string line;getline(cin, line);
}void loadDataFromFile(vector<AccountItem> &items){ifstream input(FILENAME);AccountItem item;while(input>> item.itemType >> item.amount >> item.detail){items.push_back(item);}input.close();
}