1. 介紹
需要做移動操作的情境很多:把「下載」的檔案從暫存資料夾搬到文件資料夾、批量整理照片、用範本改名、整理郵件與日誌。檔案系統喜歡有秩序──或者至少看起來有秩序!一個好的開發者應該會去維護這種秩序。
另外這類題目常出現在面試裡:「寫個工具,把所有檔案改名並加上版本號」,或是「做個命令,把過期文件移到 archive」。我們來看看在 C# 裡怎麼做。
功能概覽
在 .NET 裡有兩種主要方式做移動或改名:用靜態方法(File.Move, Directory.Move)或用實例方法(FileInfo.MoveTo, DirectoryInfo.MoveTo)。這些方法底層做的事情幾乎一樣,主要差別在語法風格。
.NET 怎麼「看」移動與改名?
注意!「改名」和「移動」本質上是同一個操作:一個檔案或資料夾由路徑來識別。如果路徑改了,就算是物件被「移動」或被「改名」。如果你只改名字但留在同一個資料夾──那是改名;如果整個路徑都變了──那是移動。哲學一點地說,有點像薛丁格的箱子:如果你給出一個新的路徑而且同時換了名字,那它就是既被移動又被改名!
2. 檔案移動與改名:範例
我們從簡單開始,基於一個小型的檔案管理器往下講。
靜態方法 File.Move
範例:如何移動(或改名)檔案:
// 將檔案 test1.txt 移到另一個資料夾並改名
File.Move(@"C:\Temp\test1.txt", @"C:\Archive\test1_archived.txt");
如果只是想在同一個資料夾改名字:
// 只是把檔案重新命名而已
File.Move(@"C:\Temp\test1.txt", @"C:\Temp\test_renamed.txt");
就這麼簡單!.NET 會把一個路徑「解綁」然後綁到另一個路徑,前提是兩個路徑都可用而且沒有衝突。
靜態方法 Directory.Move
對資料夾來說也類似:
// 移動資料夾
Directory.Move(@"C:\Temp\OldFolder", @"C:\Temp\NewFolder");
可以在同一層改名(只換名字)或把整個資料夾搬到別的地方。
3. 實例方法: FileInfo.MoveTo 和 DirectoryInfo.MoveTo
當你已經有一個 FileInfo 或 DirectoryInfo 物件時,可以呼叫 MoveTo():
var fileInfo = new FileInfo(@"C:\Temp\test1.txt");
fileInfo.MoveTo(@"C:\Archive\test1_archived.txt");
// 資料夾:
var dirInfo = new DirectoryInfo(@"C:\Temp\OldFolder");
dirInfo.MoveTo(@"C:\Temp\NewFolder");
一樣的規則:如果新路徑是在同一個目錄但換了名字,那是改名;如果是不同目錄,那是移動。
4. 如果目標已經有檔案怎麼辦?
這就是會在最不方便的時候出問題的細節。
File.Move
如果目標路徑已經有檔案,會拋出 IOException,你的程式會丟錯:
try
{
File.Move("data.txt", "archive\\data.txt");
}
catch (IOException ex)
{
Console.WriteLine("目標檔案已存在! " + ex.Message);
}
FileInfo.MoveTo
情況一樣:如果目標檔案已存在,也會拋出 IOException。
Directory.Move
資料夾的情況更嚴格:如果目標資料夾已經存在,會拋出例外。不能把資料夾移到一個已被占用的目標位置。
重點:在寫入目標前,務必檢查那裡有沒有同名的檔案或資料夾!
// 先冷靜檢查目標路徑有沒有檔案
if (!File.Exists("archive\\data.txt"))
{
File.Move("data.txt", "archive\\data.txt");
}
else
{
Console.WriteLine("目標檔案已存在!");
}
5. 有用的小提示
資料夾改名範例
var oldDir = @"C:\Projects\OldReport";
var newDir = @"C:\Projects\NewReport";
// 檢查新資料夾不存在且舊資料夾存在
if (Directory.Exists(oldDir) && !Directory.Exists(newDir))
{
Directory.Move(oldDir, newDir);
Console.WriteLine("資料夾已成功改名。");
}
else
{
Console.WriteLine("錯誤:來源資料夾不存在或目標已存在。");
}
包含內部內容的改名
資料夾的所有內容(子資料夾、檔案)會隨著一起移動,不需要額外指令!很方便:一個命令就能把有任意深度結構的資料夾搬走。
最佳實務與 Path 類的實用方法
在改名或移動檔案時,使用 System.IO.Path 的方法來正確組路徑或改檔名是很重要的:
- 取得不含副檔名的檔名: Path.GetFileNameWithoutExtension(path)
- 只改副檔名: Path.ChangeExtension(path, ".bak")
- 用不同名稱組成新路徑: Path.Combine(Path.GetDirectoryName(path), "newname.txt")
string oldPath = @"C:\Reports\2023_Final.docx";
string newName = "2023_Archive.docx";
string newPath = Path.Combine(Path.GetDirectoryName(oldPath)!, newName);
File.Move(oldPath, newPath);
6. 重要細節與常見錯誤
權限與鎖定
如果你沒有某個路徑的寫入權限,會拋出 UnauthorizedAccessException。類似地,如果檔案被其他程序鎖住(例如在 Word 裡開著),也會出錯。這是很常見的狀況。
跨不同磁碟或卷的移動
基本上 File.Move 在同一個卷/磁碟內會做「快速改名」操作。若你把檔案從 C 磁碟移到 D 磁碟,.NET 會自動做複製再刪除原檔的流程。如果目標磁碟空間不足或中途失敗,原始檔案會保留在原位。
有趣的事實:幾乎所有現代檔案系統在同一個卷內支援快速改名/移動,而跨卷就是複製 + 刪除的策略。
移動整個資料夾(Directory.Move)
不能把資料夾移到它自己的子資料夾裡(例如把 C:\Data 移到 C:\Data\Archive)──這會出錯。
怎麼安全地移動?
在任何移動操作之前,特別是批量處理,先檢查來源是否存在(File.Exists, Directory.Exists)以及目標是否會衝突。別忘了用 try-catch:檔案系統很任性,可能在最意想不到的時候丟錯誤。
GO TO FULL VERSION