(C++)任务管理系统(文件存储)(正式版)(迭代器)(list列表基础教程)(STL基础知识)
目录
前言:
源代码:
代码解析:
一.头文件和命名空间
4. using namespace std; - 命名空间
可视化比喻:建造房子 🏠
三.print_task()函数
1. 函数定义
2. 计数器初始化
3. 空列表检查
4. 打印标题
5. 遍历任务列表
6. 打印任务总数
7. 函数结束
可视化执行流程
四.add_task()函数
1. 创建临时变量
2. 显示提示信息
3. 获取用户输入
4. 添加任务到列表
5. 操作反馈
6. 打印更新后的列表
可视化执行流程
五.add_urgent_task()函数
函数分解说明(与普通任务添加对比)
1-3. 输入部分(相同)
4. 关键区别:添加位置
5-7. 反馈与结束(相同)
可视化执行流程(对比普通任务)
六.complete_task()函数
1. 空列表检查
2. 获取第一个任务
3. 移除第一个任务
4. 显示完成信息
5. 打印更新后的列表
可视化执行流程
七.主函数
八.文件存储(重点)
保存文件 详细分步解释
1. 创建并打开文件
2. 检查文件是否成功打开
3. 写入表头(标题行)
4. 遍历任务列表并写入文件
5. 关闭文件
6. 提示用户操作完成
可视化执行流程
文件内容示例
关键概念解释
文件流类型
读取文件 详细分步解释
1. 打开文件
2. 检查文件是否成功打开
3. 清空当前任务列表
4. 读取并跳过表头行
5. 逐行读取数据
5.1 循环读取每一行
5.2 统计行数
5.3 跳过空行
5.4 添加任务到列表
5.5 错误处理
6. 关闭文件
7. 输出加载结果
可视化执行流程
文件内容与加载结果
关键概念解释
文件读取方法
错误处理机制
清空列表的重要性
前言:
本篇博客建立在上篇博客(C++)任务管理系统(正式版)(迭代器)(list列表基础教程)(STL基础知识)-CSDN博客
的基础上,新增了文件存储,本章重点解释文件存储。前面的内容都是上一篇博客的内容。
源代码:
#include <iostream>
#include <list>
#include <string>
#include <fstream>using namespace std;void menu(){cout<<"\n===== 任务管理系统 ====="<<endl;cout<<"1.添加普通任务"<<endl;cout<<"2.添加紧急任务"<<endl;cout<<"3.完成一个任务"<<endl;cout<<"4.打印剩余任务"<<endl;cout<<"0.保存并退出 "<<endl;cout<<"请输入你的选择:"<<endl;
}void print_task(list<string>& task_list){int count=0;if(task_list.empty()){cout<<"暂时没有任务"<<endl;return;}cout<<"\n===== 当前剩余任务 ====="<<endl;for(auto& task:task_list){ cout<<task<<endl;count++;}cout<<"当前任务数:"<<count<<endl;return;
}void add_task(list<string>& task_list){string a;cout<<"请输入一个普通任务:"<<endl;cin>>a;task_list.push_back(a);cout<<"添加成功!"<<endl;print_task(task_list);return;
}void add_urgent_task(list<string>& task_list){string a;cout<<"请输入一个紧急任务:"<<endl;cin>>a;task_list.push_front(a);cout<<"添加成功!"<<endl;print_task(task_list);return;
}void complete_task(list<string>& task_list){if(task_list.empty()){cout<<"暂时没有任务"<<endl;return;}string completetask=task_list.front();task_list.pop_front();cout<<"完成任务:"<<completetask<<endl;print_task(task_list);return;
}void save_file(list<string>& task_list){//1.打开文件ofstream outfile("task.txt");//2.检查文件是否打开if(!outfile){cerr<<"无法打开文件"<<endl;return;}//3.打印表头outfile<<"===== 当前剩余任务 ====="<<endl;//4.遍历每条记录for(auto& str:task_list){outfile<<str<<endl;}outfile.close();cout<<"数据已经写入文件task.txt"<<endl;
}void load_file(list<string>& task_list){//1.打开文件ifstream infile("task.txt");//2.检查是否打开文件if(!infile){cerr<<"无法打开文件"<<endl;return;}//3.清空当前数据task_list.clear();//4.获取并跳过表头string header;getline(infile,header);//5.逐行读取数据string line;int line_count=0;int success_count=0;while(getline(infile,line)){line_count++;if(line.empty()){continue;}try{task_list.emplace_back(line);success_count++;}catch(const exception& e){cerr << "解析第 " << line_count << " 行时出错: " << e.what() << endl;cerr << "内容: " << line << endl;}}//6.关闭文件infile.close();//7.输出加载结果if (line_count - success_count > 0) {cout << "警告:有 " << (line_count - success_count) << " 条记录未成功加载" << endl;}
}int main(){int choice;list<string> task_list;load_file(task_list);while(true){menu();cin>>choice;switch (choice) {case 0:save_file(task_list);cout<<"感谢使用,再见"<<endl;return 0;case 1:add_task(task_list);break;case 2:add_urgent_task(task_list);break;case 3:complete_task(task_list);break;case 4:print_task(task_list);break;default:cout<<"输入有误 请重新选择!"<<endl;break;}}return 0;
}
代码解析:
一.头文件和命名空间
//头文件
#include <iostream>//输入流和输出流
#include <list>//list列表
#include <string>//字符串的使用
#include <fstream>//文件存储using namespace std;//命名空间
1. #include <iostream>
- 输入输出功能
-
作用:让程序能够进行输入(键盘)和输出(屏幕)操作
-
包含的关键功能:
-
cout
:用于向屏幕输出文本(如cout << "Hello"
) -
cin
:用于从键盘获取输入(如cin >> name
) -
endl
:用于换行(相当于按回车键)
-
-
类比:就像给电脑安装了"眼睛"(输入)和"嘴巴"(输出)
2. #include <list>
- 链表容器
-
作用:提供双向链表(list)数据结构
-
包含的关键功能:
-
list<int>
:创建整数链表 -
push_back()
:在末尾添加元素 -
push_front()
:在开头添加元素 -
pop_back()
:删除末尾元素 -
pop_front()
:删除开头元素
-
-
特点:适合频繁在任意位置插入/删除元素
-
类比:像一根可以随意插入珠子的珍珠项链
3. #include <string>
- 字符串处理
-
作用:提供字符串操作功能
-
包含的关键功能:
-
string
:创建字符串变量(如string name = "Alice"
) -
+
:拼接字符串(如"Hello" + name
) -
size()
:获取字符串长度 -
>>
/<<
:输入输出字符串
-
-
类比:给电脑安装处理文字的能力
4. using namespace std;
- 命名空间
-
作用:避免每次都要写
std::
前缀 -
效果对比:
// 不使用命名空间 std::cout << "Hello"; std::list<int> numbers;// 使用命名空间 cout << "Hello"; list<int> numbers;
-
为什么需要:C++标准库的所有功能都在
std
命名空间中 -
新手注意:在小型程序中使用没问题,但在大型项目中可能引起命名冲突
可视化比喻:建造房子 🏠
-
#include <iostream>
→ 门窗系统(输入输出通道) -
#include <list>
→ 可调整的储物架(灵活的数据容器) -
#include <string>
→ 标签和便签(文字处理工具) -
using namespace std;
→ 万能工具箱(直接使用标准工具不用找)
二.menu()函数
void menu(){cout<<"\n===== 任务管理系统 ====="<<endl;cout<<"1.添加普通任务"<<endl;cout<<"2.添加紧急任务"<<endl;cout<<"3.完成一个任务"<<endl;cout<<"4.打印剩余任务"<<endl;cout<<"0.保存并退出 "<<endl;cout<<"请输入你的选择:"<<endl;
}
主要利用输出流打印可视化可选择菜单
三.print_task()函数
void print_task(list<string>& task_list){int count=0;if(task_list.empty()){cout<<"暂时没有任务"<<endl;return;}cout<<"\n===== 当前剩余任务 ====="<<endl;for(auto& task:task_list){ cout<<task<<endl;count++;}cout<<"当前任务数:"<<count<<endl;return;
}
1. 函数定义
void print_task(list<string>& task_list)
-
void
:表示这个函数不返回任何值 -
print_task
:函数名称(打印任务) -
list<string>& task_list
:参数说明-
list<string>
:字符串链表类型 -
&
:引用传递(避免复制整个列表) -
task_list
:要打印的任务列表
-
2. 计数器初始化
int count = 0;
-
创建一个计数器变量
count
-
初始值为0(准备统计任务数量)
3. 空列表检查
if (task_list.empty()) {cout << "暂时没有任务" << endl;return;
}
-
empty()
:检查列表是否为空 -
如果列表为空:
-
打印提示信息 "暂时没有任务"
-
return
:立即结束函数(不执行后面的代码)
-
4. 打印标题
cout << "\n===== 当前剩余任务 =====" << endl;
-
\n
:先换一行(空行) -
打印装饰性的标题
5. 遍历任务列表
for (auto& task : task_list) {cout << task << endl;count++;
}
-
范围for循环:高效遍历整个列表
-
auto& task
:自动类型推导+引用(避免字符串拷贝)-
auto
:自动识别元素类型(这里是string) -
&
:引用(直接访问原始数据)
-
-
循环体:
-
cout << task << endl
:打印任务内容并换行 -
count++
:计数器+1(统计任务数量)
-
6. 打印任务总数
cout << "当前任务数:" << count << endl;
-
输出统计结果
-
endl
:换行并刷新输出缓冲区
7. 函数结束
return;
-
显式结束函数(void函数可以省略)
可视化执行流程
开始
↓
创建计数器 count=0
↓
检查任务列表是否为空? → 是 → 打印"暂时没有任务" → 结束
↓
否
↓
打印标题 "===== 当前剩余任务 ====="
↓
遍历任务列表:任务1 → 打印 → 计数+1任务2 → 打印 → 计数+1...任务N → 打印 → 计数+1
↓
打印"当前任务数:N"
↓
结束
四.add_task()函数
void add_task(list<string>& task_list){string a;cout<<"请输入一个普通任务:"<<endl;cin>>a;task_list.push_back(a);cout<<"添加成功!"<<endl;print_task(task_list);return;
}
1. 创建临时变量
string a;
-
创建一个字符串变量
a
-
作用:临时存储用户输入的任务内容
-
生命周期:只在函数执行期间存在
2. 显示提示信息
cout << "请输入一个普通任务:" << endl;
-
cout
:输出到屏幕 -
提示用户需要输入什么内容
-
endl
:换行并刷新输出缓冲区
3. 获取用户输入
cin >> a;
-
cin
:从键盘获取输入 -
>>
:输入操作符 -
用户输入的内容会存储到变量
a
中 -
注意:这种方式只能读取单个单词(遇到空格会停止)
4. 添加任务到列表
task_list.push_back(a);
-
task_list
:我们要修改的任务列表 -
.push_back()
:list 的成员函数,在末尾添加元素 -
a
:用户输入的任务内容 -
效果:任务被添加到列表的最后位置
5. 操作反馈
cout << "添加成功!" << endl;
-
给用户明确的反馈,表示操作已完成
-
良好的用户体验设计
6. 打印更新后的列表
print_task(task_list);
-
调用之前定义的打印函数
-
显示添加新任务后的完整列表
-
让用户直观看到变化
可视化执行流程
开始
↓
创建临时变量a
↓
显示提示:"请输入一个普通任务:"
↓
等待用户输入 → 用户输入"写作业"
↓
将"写作业"存入变量a
↓
将a的内容添加到task_list末尾
↓
显示"添加成功!"
↓
调用print_task显示更新后的列表
↓
结束
五.add_urgent_task()函数
函数分解说明(与普通任务添加对比)
1-3. 输入部分(相同)
-
创建临时变量
a
-
显示提示信息(但内容改为"紧急任务")
-
使用
cin >> a
获取输入(同样有空格限制问题)
4. 关键区别:添加位置
task_list.push_front(a); // 添加到列表开头
-
普通任务函数:
push_back()
→ 添加到末尾 -
紧急任务函数:
push_front()
→ 添加到开头 -
这正是链表的优势所在:在开头添加元素非常高效(O(1)时间复杂度)
5-7. 反馈与结束(相同)
-
显示添加成功提示
-
调用打印函数显示更新后的列表
-
显式返回
可视化执行流程(对比普通任务)
普通任务添加流程:
开始 → 输入任务 → 添加到列表末尾 → 打印列表紧急任务添加流程:
开始 → 输入任务 → 添加到列表开头 → 打印列表
六.complete_task()函数
1. 空列表检查
if(task_list.empty()) {cout << "暂时没有任务" << endl;return;
}
-
安全防护:防止在空列表上操作导致程序崩溃
-
empty()
:检查列表是否为空 -
如果为空:显示提示信息并直接退出函数
2. 获取第一个任务
string completetask = task_list.front();
-
front()
:获取列表的第一个元素(但不移除) -
将任务内容保存到变量
completetask
中 -
这样可以在移除后还能显示已完成的任务
3. 移除第一个任务
task_list.pop_front();
-
pop_front()
:移除列表的第一个元素 -
重要特性:这是链表的高效操作(O(1)时间复杂度)
-
执行后:列表长度减少1,后续任务向前移动
4. 显示完成信息
cout << "完成任务:" << completetask << endl;
-
给用户明确反馈
-
显示具体完成了哪个任务
-
提升用户体验
5. 打印更新后的列表
print_task(task_list);
-
调用之前定义的打印函数
-
显示移除任务后的最新列表状态
-
让用户看到当前剩余任务
可视化执行流程
开始
↓
检查列表是否为空?是 → 显示"暂时没有任务" → 结束否 → 继续
↓
获取第一个任务内容 → 存入completetask
↓
从列表中移除第一个任务
↓
显示"完成任务:XXX"
↓
打印更新后的任务列表
↓
结束
七.主函数
int main(){int choice;list<string> task_list;load_file(task_list);while(true){menu();cin>>choice;switch (choice) {case 0:save_file(task_list);cout<<"感谢使用,再见"<<endl;return 0;case 1:add_task(task_list);break;case 2:add_urgent_task(task_list);break;case 3:complete_task(task_list);break;case 4:print_task(task_list);break;default:cout<<"输入有误 请重新选择!"<<endl;break;}}return 0;
}
八.文件存储(重点)
保存文件 详细分步解释
1. 创建并打开文件
ofstream outfile("task.txt");
-
ofstream
:输出文件流类(Output File Stream) -
outfile
:我们创建的文件流对象名(可自定义) -
"task.txt"
:要保存的文件名(可以是相对路径或绝对路径) -
作用:尝试创建/打开名为 "task.txt" 的文件准备写入
2. 检查文件是否成功打开
if(!outfile) {cerr << "无法打开文件" << endl;return;
}
-
!outfile
:检查文件流状态(是否成功打开) -
可能失败的原因:
-
文件被其他程序锁定
-
磁盘空间不足
-
没有写入权限
-
路径不存在
-
-
cerr
:标准错误输出(通常显示为红色) -
return
:遇到错误时提前结束函数
3. 写入表头(标题行)
outfile << "===== 当前剩余任务 =====" << endl;
-
outfile <<
:将内容写入文件(类似cout <<
输出到屏幕) -
添加描述性标题,使文件内容更易读
-
endl
:写入换行符并刷新缓冲区
4. 遍历任务列表并写入文件
for(auto& str : task_list) {outfile << str << endl;
}
-
范围for循环:遍历任务列表中的每个任务
-
auto& str
:自动类型推导+引用(高效访问每个任务) -
outfile << str
:将任务内容写入文件 -
endl
:每个任务后换行(使文件格式清晰)
5. 关闭文件
outfile.close();
-
正式关闭文件并释放系统资源
-
重要提示:虽然流对象析构时会自动关闭文件,但显式关闭是良好习惯
-
确保所有数据实际写入磁盘(不是仅存在缓冲区)
6. 提示用户操作完成
cout << "数据已经写入文件task.txt" << endl;
-
给用户明确的反馈,告知保存操作已完成
-
显示保存的文件名,方便用户查找
可视化执行流程
开始
↓
尝试打开task.txt → 失败? → 显示错误 → 结束
↓
成功打开 ↓
写入标题行
↓
遍历任务列表:任务1 → 写入文件 + 换行任务2 → 写入文件 + 换行...任务N → 写入文件 + 换行
↓
关闭文件
↓
显示成功消息
↓
结束
文件内容示例
假设任务列表包含:
-
"完成数学作业"
-
"阅读C++教材"
-
"买菜"
保存后的task.txt内容:
===== 当前剩余任务 =====
完成数学作业
阅读C++教材
买菜
关键概念解释
文件流类型
流类型 | 头文件 | 用途 |
---|---|---|
ofstream | <fstream> | 输出文件流(写入文件) |
ifstream | <fstream> | 输入文件流(读取文件) |
fstream | <fstream> | 双向文件流(读写文件) |
读取文件 详细分步解释
1. 打开文件
ifstream infile("task.txt");
-
ifstream
:输入文件流类(Input File Stream) -
infile
:文件流对象名(可自定义) -
"task.txt"
:要读取的文件名(与保存的文件名相同) -
作用:尝试打开文件准备读取内容
2. 检查文件是否成功打开
if(!infile) {cerr << "无法打开文件" << endl;return;
}
-
!infile
:检查文件是否成功打开 -
常见失败原因:
-
文件不存在(首次运行)
-
文件被其他程序占用
-
没有读取权限
-
-
显示错误信息后直接返回
3. 清空当前任务列表
task_list.clear();
-
clear()
:清空链表中的所有元素 -
目的:确保加载的是文件中的最新数据,而不是追加到现有列表
-
相当于从零开始重建任务列表
4. 读取并跳过表头行
string header;
getline(infile, header);
-
getline()
:读取整行内容(包括空格) -
header
:存储读取到的表头内容 -
作用:跳过文件中的标题行(如"===== 当前剩余任务 =====")
-
不处理表头内容,只是移动到实际数据部分
5. 逐行读取数据
string line;
int line_count = 0;
int success_count = 0;while(getline(infile, line)) {line_count++;// 跳过空行if(line.empty()) {continue;}// 尝试添加任务到列表try {task_list.emplace_back(line);success_count++;} catch(const exception& e) {// 错误处理cerr << "解析第 " << line_count << " 行时出错: " << e.what() << endl;cerr << "内容: " << line << endl;}
}
5.1 循环读取每一行
-
while(getline(infile, line))
:循环读取文件直到结束 -
getline()
:每次读取一行内容到line
变量
5.2 统计行数
-
line_count++
:记录当前处理的行号(用于错误定位)
5.3 跳过空行
-
if(line.empty()) continue;
:忽略空白行 -
目的:避免添加空任务到列表
5.4 添加任务到列表
-
task_list.emplace_back(line);
:高效添加任务 -
emplace_back
直接构造字符串,避免额外拷贝 -
success_count++
:记录成功添加的任务数
5.5 错误处理
-
try-catch
块捕获可能的异常 -
常见错误:内存不足、无效字符等
-
显示错误行号和问题内容
6. 关闭文件
infile.close();
-
释放文件资源
-
良好的编程习惯
7. 输出加载结果
if (line_count - success_count > 0) {cout << "警告:有 " << (line_count - success_count) << " 条记录未成功加载" << endl;
}
-
计算失败加载的记录数:
失败数 = 总行数 - 成功数
-
如果有失败记录,显示警告信息
-
帮助用户了解数据加载的完整性
可视化执行流程
开始
↓
尝试打开task.txt → 失败? → 显示错误 → 结束
↓
成功打开 ↓
清空当前任务列表
↓
读取并跳过表头行
↓
循环读取每一行:行号+1是否空行? → 是 → 跳过否 → 尝试添加到任务列表成功? → 成功计数+1失败? → 显示错误信息
↓
关闭文件
↓
计算并显示加载结果
↓
结束
文件内容与加载结果
假设task.txt内容:
===== 当前剩余任务 =====
完成数学作业
阅读C++教材
买菜
无效任务!@#$%
加载结果:
-
跳过表头行
-
添加"完成数学作业"(成功)
-
添加"阅读C++教材"(成功)
-
添加"买菜"(成功)
-
尝试添加"无效任务!@#$%"(可能失败)
-
显示警告:有1条记录未成功加载
关键概念解释
文件读取方法
方法 | 说明 | 示例 |
---|---|---|
getline() | 读取整行(包括空格) | getline(infile, line) |
>> 操作符 | 读取单词(遇到空格停止) | infile >> word |
read() | 读取原始字节(二进制数据) | infile.read(buffer, size) |
错误处理机制
try {// 可能出错的代码
} catch(const exception& e) {// 错误处理cerr << "错误: " << e.what() << endl;
}
-
try
:尝试执行可能出错的代码块 -
catch
:捕获并处理特定类型的异常 -
exception
:所有标准异常的基类 -
e.what()
:获取错误描述信息
清空列表的重要性
task_list.clear();
-
防止重复加载:如果不清空,每次加载都会追加到现有列表
-
确保数据一致性:文件内容是唯一数据源
注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!