CodeGym /課程 /C# SELF /C# 檔案操作: System.IO.File

C# 檔案操作: System.IO.File 類別

C# SELF
等級 35 , 課堂 2
開放

1. File 類別:簡單又有效率

System.IO.File 這個 class 就是「一個操作一個方法」的概念。它提供一堆高階方法,把跟作業系統互動的複雜邏輯都包起來,讓你可以專心寫自己的功能。

File 這個 class 到底能幹嘛?

  • 寫入文字或任意二進位資料 到檔案。
  • 讀取檔案內容(整個或一行一行,針對文字檔)。
  • 複製、刪除、重新命名 已存在的檔案。
  • 檢查檔案是否存在(根據你給的路徑)。
  • 操作檔案屬性(像是建立日期、大小——要用相關的 class)。

為什麼這麼方便?
File 的所有方法都是 static 的。意思就是你不用 new 一個物件,直接呼叫方法就好,例如:

File.WriteAllText("data.txt", "哈囉,檔案!");

這種寫法很像 Console 這個 class:超直覺、超簡單,不用「建立」什麼,也不用管 stream。

如果你想要一個萬用又好用的檔案操作工具,System.IO.File 就是你的好朋友。它有一堆方法可以讀、寫、複製、刪除檔案,根本就是檔案系統的瑞士刀。

怎麼用 File

File 這個 class 的方法都是 static,所以不用 new 物件,直接寫 File.什麼() 就行。這跟 Console.WriteLine() 很像,只是這次是針對檔案。

string text = "哈囉,世界!";
string path = "hello.txt";

// 寫入文字到檔案(如果檔案已經存在會覆蓋!)
File.WriteAllText(path, text);

// 讀取整個檔案成一個字串
string readText = File.ReadAllText(path);

Console.WriteLine("檔案內容: " + readText);

小撇步: 如果你一直狂用 ReadAllTextWriteAllText,十次八次都這樣寫,那你大概做錯了什麼——這些方法適合簡單情境跟小檔案。

File 常用方法

方法 說明
File.Exists(path)
檢查指定路徑的檔案是否存在
File.Delete(path)
刪除檔案
File.Copy(src, dest)
複製檔案
File.Move(src, dest)
移動(或重新命名)檔案
File.ReadAllText(path)
把整個檔案讀成一個字串
File.WriteAllText(path, t)
把字串寫進檔案(如果已經有會覆蓋)
File.ReadAllLines(path)
把檔案每一行讀成字串陣列
File.WriteAllLines(path, a)
把字串陣列寫進檔案(每個元素一行)
File.ReadAllBytes(path)
把檔案所有位元組讀成 byte 陣列
File.WriteAllBytes(path, b)
把 byte 陣列寫進檔案

2. 文字檔案的寫入與讀取

來看一下 File class 最常用的方法跟實際範例。要用這些方法,檔案開頭一定要有 using System.IO;

using System;
using System.IO; // 用 File class 必備
using System.Linq; // ReadLines 可能會用到

整個檔案一次讀

這是最常見的需求:把文字資料存進檔案,或是整個讀回來。

寫一行(或全部文字)到檔案:File.WriteAllText() 這個方法會在指定路徑建立新檔案。如果檔案已經存在,內容會被新文字完全覆蓋

string myFilePath = "notes.txt";
string textContent = "今天很適合學 C#。\n別忘了寫作業。";

// 把整個文字寫進檔案
File.WriteAllText(myFilePath, textContent); 
Console.WriteLine($"檔案 '{myFilePath}' 已經建立/覆蓋完成。");

整個檔案讀成一個字串:File.ReadAllText() 這個方法會把整個文字檔案內容讀成一個大字串。

string myFilePath = "notes.txt";

// 注意:讀檔前一定要先檢查檔案是否存在,才不會出錯!
if (File.Exists(myFilePath))
{
    string content = File.ReadAllText(myFilePath);
    Console.WriteLine($"檔案 '{myFilePath}' 內容:\n{content}");
}
else
{
    Console.WriteLine($"錯誤:檔案 '{myFilePath}' 找不到,無法讀取。");
}

重要提醒: File.WriteAllText()File.ReadAllText() 很適合小型或中型檔案(像是記事本、設定檔、短 log)。但如果檔案超大(幾十、幾百 MB,甚至 GB),一次把全部內容讀進記憶體會超級吃 RAM,甚至直接 OutOfMemoryException。這種情況要用更有效率的做法(下面會講)。

3. 一行一行讀檔案

如果你要處理很大的文字檔(像超大 CSV、長 log、文字資料庫),一次把整個檔案讀進記憶體根本不可能。這時候就要用 File.ReadLines()

File.ReadLines(path) 不會一次把整個檔案讀進來。它會回傳一個特殊的 collection(IEnumerable<string>),讓你需要一行才讀一行。這叫「lazy 讀取」或「streaming 讀取」。

string logFilePath = "application.log";
string text = """
INFO: 應用程式啟動了。
WARN: 模組 X 發現問題。
ERROR: 網路嚴重錯誤。
INFO: 應用程式已結束。    
""";    
    
// 假設我們有一個很多行的 log 檔
File.WriteAllText(logFilePath, text);

// 每次迴圈只會讀一行並處理
foreach (string line in File.ReadLines(logFilePath))
    Console.WriteLine($"- {line}");

優點: 檔案會分段讀取跟處理,處理大檔案時記憶體用量超低。

File.ReadLines()File.ReadAllLines() 差在哪?

  • File.ReadAllLines(path)一次把所有行都讀進記憶體,回傳 string[] 陣列。適合你要一次處理全部資料(像排序、搜尋)。
  • File.ReadLines(path):回傳 IEnumerable<string>,讓你一行一行讀。只有你真的要那一行時才會去讀,非常適合超大檔案。

4. 寫入字串陣列

有時候你有一堆字串(像某個 collection),想要每一行都寫進檔案。

File.WriteAllLines(path, contents)

這個方法可以把字串陣列(或任何字串 collection)每一行都寫進檔案。跟 WriteAllText() 一樣,如果檔案已經存在會覆蓋。

string membersFilePath = "participants.txt";
string[] names = { "伊凡 彼得羅夫", "瑪麗亞 西多洛娃", "謝爾蓋 科茲洛夫", "安娜 庫茲涅佐娃" };

// 陣列每一行都會寫進檔案
File.WriteAllLines(membersFilePath, names);

// 檢查內容
Console.WriteLine($"'{membersFilePath}' 內容:");
foreach (string line in File.ReadLines(membersFilePath))
    Console.WriteLine($"- {line}");

5. 處理二進位檔案

File 不只處理文字,也有很方便的二進位資料讀寫方法。像是存設定檔、圖片、音訊或任何不是純文字的資料都很實用。

寫入 byte 陣列:File.WriteAllBytes() 會建立或覆蓋檔案,把你給的 byte 陣列全部寫進去。

string binaryDataPath = "settings.bin";
// 假設這是二進位資料:圖片、影片、隨便什麼
byte[] settingsData = { 0x01, 0x0A, 0x2C, 0xFF, 0x00, 0x3F }; // 一些「原始」設定

File.WriteAllBytes(binaryDataPath, settingsData);
Console.WriteLine($"二進位資料已寫入檔案 '{binaryDataPath}'。");

注意: 如果你用記事本打開 'settings.bin',只會看到亂碼,因為這些 byte 不是給人看的。

讀取 byte 陣列:File.ReadAllBytes() 會把整個二進位檔案讀成 byte 陣列。

if (File.Exists(binaryDataPath))
{
    byte[] readSettings = File.ReadAllBytes(binaryDataPath);
    Console.WriteLine($"從檔案 '{binaryDataPath}' 讀到 {readSettings.Length} 個位元組。");

    Console.Write("讀到的 byte(16 進位):");
    foreach (byte b in readSettings)
        Console.Write($"{b:X2} "); // :X2 會把 byte 轉成兩位數 16 進位

    Console.WriteLine();
}
else
{
    Console.WriteLine($"錯誤:二進位檔案 '{binaryDataPath}' 找不到。");
}

這些方法很適合存小型二進位資料,像快取小圖片或序列化物件。

6. 磁碟上的檔案管理

File 也有一堆管理檔案的方法。

檢查檔案是否存在:File.Exists(path) 如果指定路徑有檔案會回傳 true,否則 false。建議你在讀、刪、其他操作前都先檢查,才不會出錯。

string myFileToCheck = "report.txt";
// 先建立一個檔案
File.WriteAllText(myFileToCheck, "測試報告。");

if (File.Exists(myFileToCheck))
{
    Console.WriteLine($"檔案 '{myFileToCheck}' 找到了。");
    // 現在可以安全地讀、刪等操作
}
else
{
    Console.WriteLine($"檔案 '{myFileToCheck}' 找不到。");
}

刪除檔案:File.Delete(path) 會直接把檔案從磁碟刪掉。超級危險,這動作無法還原!

string fileToDelete = "temp_file.txt";
// 先建立一個檔案
File.WriteAllText(fileToDelete, "這個檔案會被刪掉。");

if (File.Exists(fileToDelete))
{
    File.Delete(fileToDelete);
    Console.WriteLine($"檔案 '{fileToDelete}' 已經刪除。");
}
else
{
    Console.WriteLine($"檔案 '{fileToDelete}' 已經不存在或早就被刪了。");
}

7. 檔案操作時的例外狀況

檔案操作都要跟作業系統和外部資源互動,所以很容易出錯。

一定要處理例外(錯誤)! 操作檔案時可能會遇到一堆問題,你的程式要能好好處理,不然直接「爆掉」。常見情況:

  • FileNotFoundException:你要存取的檔案根本不存在。
  • DirectoryNotFoundException:你要找的資料夾不存在。
  • UnauthorizedAccessException:你的程式沒權限讀、寫、刪檔案/資料夾(像是系統檔或受保護區域)。
  • IOException:一般 I/O 錯誤,像檔案被其他程式鎖住、磁碟沒空間等。

一定要用 try-catch 來安全操作檔案。這樣可以「抓」到錯誤,然後做適當處理(像顯示訊息、寫 log)。

string sensitiveFilePath = "secret_data.txt";

try
{
    // 嘗試讀一個可能不存在或沒權限的檔案
    string secretContent = File.ReadAllText(sensitiveFilePath);
    Console.WriteLine($"讀到秘密內容:{secretContent}");
}
catch (FileNotFoundException) // 特定錯誤:檔案找不到
{
    Console.WriteLine($"錯誤:檔案 '{sensitiveFilePath}' 找不到。請確認路徑正確。");
}
catch (IOException ex) // 一般 I/O 錯誤(像檔案被鎖住)
{
    Console.WriteLine($"操作檔案 '{sensitiveFilePath}' 時發生 I/O 錯誤:{ex.Message}");
}
catch (Exception ex) // 其他沒想到的錯誤
{
    Console.WriteLine($"發生未知錯誤:{ex.Message}");
}

8. 什麼時候 System.IO.File 不適合?

雖然 File 超萬用,但有些情境不適合用它:

超大檔案(幾十 GB 以上): ReadAllText()/ReadAllBytes() 會把整個檔案讀進記憶體。超大檔案會直接 OutOfMemoryException。這時要用 File.ReadLines()(文字)或直接用 streamFileStream)分段讀寫。(stream 之後會講)

要細緻控制檔案存取: 如果你要很細的控制(像獨佔存取、讓別人不能讀寫,或多個 process 同時讀),File 沒辦法做到。這時要用更底層的 stream class。

非同步操作: 如果你寫高效能 app,要做檔案操作又不想卡主 thread(像 web server),要用 async 方法(像 ReadAllTextAsync()),這些也是基於 stream。

只讀/寫部分檔案: 如果你只想讀某一段或寫到檔案中間,File 的方法不行。這時要直接用 stream。

9. 小結

為什麼 System.IO.File 這麼受歡迎?

  • 簡單又精簡: static 方法讓你一兩行就能做完複雜操作,超省事,程式碼也好讀。
  • 自動管理資源: File 會自動開關檔案,不用你自己處理(手動很容易出錯)。
  • 日常任務超萬用: 大部分小型、中型文字或二進位檔案的需求,File 都夠用。
  • 很常見: 大部分實務專案跟面試都會希望你會用 File 的方法。
  • 複雜都藏起來: 所有方法都把跟作業系統互動的複雜邏輯包起來,給你一個乾淨又直覺的介面。

常見問題與補充

File.ReadAllText() 能處理多大的檔案?
幾十 MB(< 100 MB)以下通常沒問題。如果檔案更大(幾百 MB、GB),用 File.ReadAllText() 可能會爆記憶體(OutOfMemoryException)。這時要用 File.ReadLines() 一行一行讀,或用更進階的 streaming 工具(之後會學)。

可以在不存在的資料夾裡建立/寫檔案嗎?
不行。File.WriteAllText()File.WriteAllBytes() 等方法不會自動建立父資料夾。如果你指定 C:\MyAppData\Logs\current.log,但 MyAppDataLogs 沒有,會直接 DirectoryNotFoundException。你要先用 System.IO.Directory 的方法(像 Directory.CreateDirectory("myfolder"))確保資料夾存在。

怎麼查檔案建立、修改日期或大小?
要拿到更詳細的檔案資訊(像建立日期、最後修改、大小、屬性(隱藏、唯讀等)),用 System.IO.FileInfo 這個 class。它有一堆很實用的屬性。

2
任務
C# SELF, 等級 35, 課堂 2
上鎖
建立並寫入資料到檔案
建立並寫入資料到檔案
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION