【C++ 筆記】檔案處理 - part 27

很感謝你點進來這篇文章。

你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!

至此之前,我們已經有學過 <iostream> 的標準輸入輸出流 cin, cout。

那本篇要學的就是 <fstream> 裡面的三種流,ofstream, ifstream, fstream

基本檔案處理(File Handing)

<fstream> 定義了三種資料型態:

表格參考:菜鳥教程

資料型態敘述
ofstream(o:Output)輸出檔案流,用於建立檔案並向檔案寫入訊息。
ifstream(i:Input)輸入檔案流,用於從檔案中讀取訊息。
fstream上述兩種資料型態的結合體,亦即同時具備兩種資料型態的功能。

要做到檔案處理,需引入 <iostream><fstream>

檔案處理的基本操作

主要分成:

  • 開啟檔案
  • 讀寫操作
  • 關閉檔案

開啟檔案

語法:

1
fstream str("filename.ext", mode);
  • str:給這個流的名字。
  • filename:檔名。
  • mode:表示要與這個檔案互動的方式。

檔案開啟模式(mode)

Table Source:https://www.geeksforgeeks.org/cpp/file-handling-c-classes/

模式(Mode)說明(Description)
ios::in以讀取模式開啟檔案。若檔案不存在,將無法開啟。
ios::out以寫入模式開啟檔案:內部的串流緩衝區支援輸出操作。
ios::binary以二進位模式進行操作,而非文字模式。
ios::ate輸出的起始位置會設在檔案結尾。
ios::app所有輸出操作都會發生在檔案末端,並附加於現有內容之後。
ios::trunc開啟檔案時會清除其原有內容(若存在)。

這些模式可透過 OR 運算子 | 同時做到兩種操作:

fstream str("file.txt", ios::in | ios::out)


ios::in 範例:

將 example.txt 與 eg.cpp 放到同目錄下。

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// eg.cpp
#include <iostream>
#include <fstream>

using namespace std;

int main(){
fstream file("example.txt", ios::in);
if (file.is_open()){ // is_open() 回傳 bool 檢查檔案是否成功開啟
string line;
getline(file, line);
cout << "example.txt 的內容是:" << line << endl;
file.close(); // 關閉檔案
}
else{
cout << "檔案可能不存在" << endl;
}
return 0;
}

使用 Code::Blocks 的輸出結果為:

image


ios::out 寫入檔案(會清空原本的內容):

原本 example.txt 寫的是 Big Handsome Boy,透過以下程式碼後,會覆蓋掉原本的字。

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <fstream>
using namespace std;

int main() {
fstream file("example.txt", ios::out);
file << "這是新資料。" << endl;
file.close();
return 0;
}

執行後:

image


ios::ate 開啟檔案後會跳到檔案尾:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <fstream>
using namespace std;

int main() {
fstream file("example.txt", ios::in | ios::out | ios::ate);
if (file.is_open()) {
file << "附加內容(位置不強制)" << endl;
file.close();
}
return 0;
}

此時檔案的寫入就會從檔案結尾開始:

image


ios::app 強制所有寫入附加到檔案結尾:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <fstream>
using namespace std;

int main() {
fstream file("example.txt", ios::app);
file << "這行會附加在檔案末尾。" << endl;
file.close();
return 0;
}

執行後:

image


ios::trunc 清空檔案:

ios::out 本身預設就帶有 ios::trunc,若加上 ios::app 則不會清空。

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <fstream>
using namespace std;

int main() {
ofstream file("example.txt", ios::out | ios::trunc);
file << "檔案原有內容被清除。" << endl;
file.close();
return 0;
}

讀取檔案

  1. 透過自定義的 fstream 名稱加上 >> 運算子可讀取檔案內的內容給變數。

example.txt:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>

using namespace std;

int main(){

fstream file("example.txt");

string s;

file >> s;

cout << "讀取到的是 : " << s;

return 0;
}

Output:

image

這種方式就如同用 cin 一樣,一旦讀到含有空格的字串,那就只會讀到第一個字串。

  1. getline(file, s) 去讀。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>

using namespace std;

int main(){

fstream file("example.txt");

string s;

getline(file, s);

cout << "讀取到的是 : " << s;

return 0;
}

Output:

image

關閉檔案

當檔案處理完畢後,很重要的一件事是:務必要關上檔案。

語法:

file.close()

為什麼呢?除了長時間開啟檔案,一直在那占用資源以外,關閉檔案也能以避免記憶體洩漏、資料遺失等問題。

檔案處理上的錯誤

為什麼要知道這些錯誤?主要是防範於未然,有時候像是忘記要讀取,那這時候可以加上一個自定義的讀取錯誤來提醒自己要加上讀取。

檔案開啟錯誤

is_open() 函式去判斷是否能開啟檔案。

如下範例(Source from GeeksForGeeks):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <bits/stdc++.h>
using namespace std;

int main() {
fstream file("nonexistent_file.txt", ios::in);

// Check if the file is opened
if (!file.is_open()) {
cerr << "Error: Unable to open file!" << endl;
return 1;
}

file.close();
return 0;
}

如果目錄下沒有 noneexistent_file.txt 文件的話,就會直接輸出這行:

1
Error: Unable to open file!

註:cerr 被稱為標準錯誤輸出,與原本的 cout 相比,他沒有緩衝就直接輸出了,如其名,主要用於輸出錯誤、警告等訊息。

讀寫失敗

if (!getline(file, s)) 判斷是否有資料讀取。

稍微改一下的範例(Source from GeeksForGeeks):

如下程式碼中,只有給 ios::out,而沒有 ios::in,也不做任何讀取的動作,那當然就會出錯囉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <bits/stdc++.h>

using namespace std;

int main(){

fstream file("example.txt", ios::out);

if (!file.is_open()) {
cerr << "Error: Unable to open file!" << endl;
return 1;
}

string s;

if (!getline(file, s)){
cerr << "Error: Failed to read data." << endl;
}

file.close();

return 0;
}

檔案結尾(EOF)錯誤

臭名昭著的 EOF,全名為 End of file,競程上時常碰到的對手。

可用 eof() 函式判斷是否有到 EOF。

範例(Source from GeeksForGeeks):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <bits/stdc++.h>
using namespace std;

int main() {
ifstream file("GFG.txt");

if (!file.is_open()) {
cerr << "Error: Unable to open file!" << endl;
return 1;
}
string line;
while (getline(file, line))
cout << line << endl;

// Check for eof
if (file.eof())
cout << "Reached end of file." << endl;
else
cerr << "Error: File reading failed!" << endl;

file.close();
return 0;
}

總結

基本檔案處理(File Handling)

需包含標頭檔 <iostream><fstream>

<fstream> 定義三種資料型態:

  • ofstream:輸出檔案流,用於建立檔案並寫入資料。
  • ifstream:輸入檔案流,用於從檔案讀取資料。
  • fstream:結合輸入與輸出功能的檔案流。

檔案開啟與模式

開啟檔案語法:

1
fstream file("filename.ext", mode);

常用模式(可用 | 組合):

  • ios::in:讀取模式,檔案不存在則開啟失敗。
  • ios::out:寫入模式,會清空檔案內容(除非搭配 ios::app)。
  • ios::binary:二進位模式。
  • ios::ate:開啟後指標移到檔案尾,可從尾端開始寫入。
  • ios::app:所有寫入附加在檔案末尾。
  • ios::trunc:開啟時清空檔案內容(ios::out 預設帶此模式)。

讀寫操作

  • 讀取:

    1. 使用 >> 運算子讀取資料(遇空白停止)。
    2. 使用 getline(file, string_var) 讀取整行文字。
  • 寫入:

    • 使用 << 運算子寫入資料。

關閉檔案

使用 file.close() 關閉檔案,釋放資源,避免資料遺失或記憶體洩漏。

錯誤處理

  • 開啟檔案失敗可用 is_open() 判斷並提示錯誤。
  • 讀取失敗可用 if (!getline(file, s)) 判斷。
  • 使用 eof() 判斷是否到達檔案結尾,避免讀取過頭。

參考資料

C++中cout和cerr的区别?_cerr<<-CSDN博客

C++ cerr object | W3Schools

File Handling through C++ Classes - GeeksforGeeks

C++ 文件和流 | 菜鸟教程