需求
- 实现一个基于命令行界面的简单记账软件
具体功能
- 记账功能,用来记录每一笔收入和支出
- 查询功能,用来查询当前的所有账目,并统计总收支;可以选择查询全部账目、查询收入和查询支出
- 软件采用分级菜单形式;每一级菜单应该有“返回主菜单”功能
数据结构设计
- 每一条账目数据,都应该包含收支类型、金额、备注三部分,可以构建一个结构体类型 Accountltem 来表示
- 保存在文件中的数据,读取之后应该是一个 Accountltem 类型的 vector 容器
流程控制设计
用户不选择退出,程序就不会结束,所以应该用一个while循环来处理整个流程;当用户确认退出时,更改一个标志位,用来退出循环每一级菜单做键盘选择后,可以用 switch 分支语句来处理不同的功能模块,可以包装成函数
代码
- common.h
#pragma once
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#define INCOME "收入"
#define EXPAND "支出"
#define FILENAME "D:\\data\\AccountBook.txt"
using namespace std;
// 通用功能性函数声明
// 绘制菜单的函数
void showMainMenu();
void showAccountingMenu();
void showQueryMenu();
// 读取键盘输入进行合法性校验的函数
char readMenuSelection(int options);
char readQuitConfirm();
int readAmount();
- account_item.h
#pragma once
#include "common.h"
// 定义结构体类型
struct AccountItem {
string itemType;
int amount;
string detail;
};
// 针对账目数据进行操作的函数声明
// 加载账目数据
void loadDataFromFile(vector<AccountItem>& items);
// 记账
void accounting(vector<AccountItem>& items);
void insertIntoFile(AccountItem& item);
void income(vector<AccountItem>& items);
void expand(vector<AccountItem>& items);
// 查询
void query(const vector<AccountItem>& items);
void queryItems(const vector<AccountItem>& items);
void queryItems(const vector<AccountItem>& items, const string itemType);
void printItem(const AccountItem& items);
- main_account.cpp
#include "common.h"
#include "account_item.h"
int main() {
// 1. 加载文件中的账目数据
vector<AccountItem> items;
loadDataFromFile(items);
bool quit = false;
while (!quit) {
// 2. 显示主菜单
showMainMenu();
// 3. 读取键盘选择,并作合法性校验
char key = readMenuSelection(3);
switch (key) {
case '1': // 1 - 记账
showAccountingMenu();
accounting(items);
break;
case '2': // 2 - 查询
showQueryMenu();
query(items);
break;
case '3': // 3 - 退出
cout << "\n确认退出? (Y/N):";
if (readQuitConfirm() == 'Y')
quit = true;
break;
default:
break;
}
cout << endl;
}
}
- menus.cpp
#include "common.h"
// 绘制菜单函数
void showMainMenu() {
system("cls");
cout << "-----------------------------------------" << endl;
cout << "|============ 欢迎使用记账薄 ============|" << endl;
cout << "| |" << endl;
cout << "|*************** 1 记账 **************|" << endl;
cout << "|*************** 2 查询 **************|" << endl;
cout << "|*************** 3 退出 **************|" << endl;
cout << "|---------------------------------------|" << endl;
cout << "\n请选择(1 - 3):";
}
void showAccountingMenu() {
cout << "-----------------------------------------" << endl;
cout << "|============= 选择记账种类 =============|" << endl;
cout << "| |" << endl;
cout << "|*************** 1 收入 **************|" << endl;
cout << "|*************** 2 支出 **************|" << endl;
cout << "|*************** 3 返回主菜单 ********|" << endl;
cout << "|---------------------------------------|" << endl;
cout << "\n请选择(1 - 3):";
}
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):";
}
- read_input.cpp
#include "common.h"
// 读取键盘输入的菜单选项,进行合法性校验
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) {
getline(cin, str);
// 进行合法性校验
if (str.size() != 1 || toupper(str[0]) != 'Y' && toupper(str[0]) != 'N')
cout << "输入错误,请重新输入(Y/N):";
else
break;
}
// 输入合法
return toupper(str[0]);
}
// 读取键盘输入的金额数,并做合法性校验
int readAmount() {
int amount;
string str;
while (true) {
getline(cin, str);
// 进行合法性校验
try {
amount = stoi(str);
break;
}
catch (invalid_argument e) {
cout << "输入错误,请正确输入数字:";
}
}
// 输入合法
return amount;
}
- operations.cpp
#include "common.h"
#include "account_item.h"
// 读取文件中的账目数据
void loadDataFromFile(vector<AccountItem>& items) {
ifstream input(FILENAME);
// 逐行读取每一条账目,包装成AccountItem
AccountItem item;
while (input >> item.itemType >> item.amount >> item.detail) {
items.push_back(item);
}
input.close();
}
//---------------------------1.记账-------------------------------
// 记账操作
void accounting(vector<AccountItem>& items) {
// 读取键盘选择,并作合法性校验
char key = readMenuSelection(3);
switch (key) {
case '1': // 1 - 收入
income(items);
break;
case '2': // 2 - 支出
expand(items);
break;
default:
break;
}
cout << endl;
}
// 记录一笔收入
void income(vector<AccountItem>& items) {
// 新构建一个AccountItemd对象
AccountItem item;
// 类型已经确认,就是收入
item.itemType = INCOME;
// 与用户交互,键盘输入金额和备注信息
cout << "\n本次收入金额:";
item.amount = readAmount();
cout << "\n备注:";
getline(cin, item.detail);
// 添加到vector中
items.push_back(item);
// 写入文件做持久化保存
insertIntoFile(item);
// 显示成功信息
cout << "\n-----------------记账成功!----------------\n" << endl;
cout << "\n请按回车键返回主菜单---" << endl;
string line;
getline(cin, line);
}
// 记录一笔支出
void expand(vector<AccountItem>& items) {
// 新构建一个AccountItemd对象
AccountItem item;
// 类型已经确认,就是收入
item.itemType = EXPAND;
// 与用户交互,键盘输入金额和备注信息
cout << "\n本次支出金额:";
item.amount = -readAmount();
cout << "\n备注:";
getline(cin, item.detail);
// 添加到vector中
items.push_back(item);
// 写入文件做持久化保存
insertIntoFile(item);
// 显示成功信息
cout << "\n-----------------记账成功!----------------\n" << endl;
cout << "\n请按回车键返回主菜单---" << endl;
string line;
getline(cin, line);
}
// 将一条账目写入文件中
void insertIntoFile(AccountItem& item) {
// 创建一个ofstream对象,以追加方式进行写入
ofstream output(FILENAME, ios::out | ios::app);
output << item.itemType << "\t" << item.amount << "\t" << item.detail << endl;
output.close();
}
// ------------------------- 2. 查询--------------------------
// 查询操作
void query(const vector<AccountItem>& items) {
// 读取键盘选择,并作合法性校验
char key = readMenuSelection(3);
switch (key) {
case '1': // 1 - 查询所有账目,并统计总收支
queryItems(items);
break;
case '2': // 2 - 查询收入,统计总收入
queryItems(items, INCOME);
break;
case '3': // 3 - 查询支出,统计总支出
queryItems(items, EXPAND);
break;
default:
break;
}
}
// 查询账目功能函数
void queryItems(const vector<AccountItem>& items) {
cout << "---------------查询结果-----------------" << endl;
cout << "\n 类型\t\t金额\t\t备注\n" << endl;
// 遍历所有账目,统计总收支
int total = 0;
for (auto item : items) {
printItem(item);
total += item.amount;
}
// 输出信息
cout << "==============================================\n" << endl;
cout << "总收支:" << total << endl;
cout << "\n请按回车键返回主菜单---" << endl;
string line;
getline(cin, line);
}
// 函数重载
void queryItems(const vector<AccountItem>& items, const string itemType) {
cout << "---------------查询结果-----------------" << endl;
cout << "\n 类型\t\t金额\t\t备注\n" << endl;
// 遍历所有账目,统计总收入或总支出
int total = 0;
for (auto item : items) {
if (item.itemType != itemType)
continue;
printItem(item);
total += item.amount;
}
// 输出信息
cout << "==============================================\n" << endl;
cout << ((itemType == INCOME) ? "总收入:" : "总支出:") << total << endl;
cout << "\n请按回车键返回主菜单---" << endl;
string line;
getline(cin, line);
}
// 打印输出一条账目信息
void printItem(const AccountItem& item) {
cout << item.itemType << "\t\t" << item.amount << "\t\t" << item.detail << endl;
}
