【C++学习系列】1.小谷记账踩坑记

文章目录

  • 前言
  • 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& 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";
        else
            break;    

    }
    return toupper(str[0]);
}

2. 几个小坑

2.1 为什么要用引用传值

// ------------1. 记账操作------------//
void accounting(vector &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)
【C++学习系列】1.小谷记账踩坑记_第1张图片

这个函数原来用的第一种方式,造成 item.detail 收到了 断行

3. 未解决的问题

我用vscode 编码的, 写入的数据 中午是乱码,但是piple走通了。

git地址:https://github.com/justinge/cpp_xiaogu

全体代码也贴一下:

#include
#include
#include
#include
#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 &items);
void expand(vector &items);
void accounting(vector &items);
void query(vector &items);
void loadDataFromFile(vector &items);
int main(){
    cout << "justin 盖建明" << endl;
    vector 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 << "主菜单" < 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 &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 &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 &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 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 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 &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 &items){
    ifstream input(FILENAME);
    AccountItem item;
    while(input>> item.itemType >> item.amount >> item.detail){
        items.push_back(item);
    }
    input.close();
}

你可能感兴趣的:(C++,c++,学习,开发语言)