目录
4. using namespace std; - 命名空间
前言:
本篇博客建立在上篇博客(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();
防止重复加载:如果不清空,每次加载都会追加到现有列表
确保数据一致性:文件内容是唯一数据源
注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!