目錄
前言:
源代碼:?
代碼解析:
一.頭文件和命名空間
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();
-
防止重復加載:如果不清空,每次加載都會追加到現有列表
-
確保數據一致性:文件內容是唯一數據源
注:該代碼是本人自己所寫,可能不夠好,不夠簡便,歡迎大家指出我的不足之處。如果遇見看不懂的地方,可以在評論區打出來,進行討論,或者聯系我。上述內容全是我自己理解的,如果你有別的想法,或者認為我的理解不對,歡迎指出!!!如果可以,可以點一個免費的贊支持一下嗎?謝謝各位彥祖亦菲!!!!!