檔案操作是 C# 開發的基礎功夫。本文彙整常見場景:取得版本資訊、處理唯讀檔案刪除、判斷存在性、逐行讀寫、以及使用 Shell API 實現「移至資源回收筒」等進階功能,並附上可直接複製的最佳實踐程式碼。
using System.Diagnostics;
string path = @"D:\TEST.DLL";
if (File.Exists(path))
{
FileVersionInfo info = FileVersionInfo.GetVersionInfo(path);
Console.WriteLine($"版本:{info.FileVersion}");
Console.WriteLine($"產品:{info.ProductName}");
Console.WriteLine($"著作權:{info.LegalCopyright}");
}
File.Delete 無法刪除唯讀檔,File.Copy(..., true) 覆蓋時也會因目標唯讀而拋例外。標準流程:重設屬性 → 刪除 → 複製。
string src = @"E:\test.txt";
string dest = @"D:\tmp\test.txt";
if (File.Exists(dest))
{
var fi = new FileInfo(dest);
if ((fi.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
fi.Attributes = FileAttributes.Normal; // 移除唯讀
File.Delete(dest);
}
File.Copy(src, dest, true); // true = 允許覆蓋
bool fileExists = File.Exists(@"C:\data.txt");
bool dirExists = Directory.Exists(@"D:\LatestVersion");
使用 StreamReader 配合 Peek() 或 ReadLine() 回傳 null 判斷結尾。
int lineCount = 0;
using (var sr = new StreamReader(@"C:\log.txt", Encoding.UTF8))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine($"[{++lineCount}] {line}");
}
}
using (var sw = new StreamWriter(@"D:\result.txt", false, Encoding.UTF8))
{
for (int i = 0; i < 10; i++)
sw.WriteLine($"這是第 {i} 行資料");
}
注意:StreamWriter 建構子第二個參數 append,false 覆蓋、true 附加。
直接 File.Delete 是永久刪除。若需「可復原」刪除,必須呼叫 Shell API。
using System;
using System.Runtime.InteropServices;
public static class ShellFileOp
{
private const uint FO_DELETE = 0x3;
private const ushort FOF_ALLOWUNDO = 0x40; // 移至資源回收筒
private const ushort FOF_NOCONFIRMATION = 0x10; // 無確認對話框
private const ushort FOF_SILENT = 0x04; // 無進度 UI
private const ushort FOF_NOERRORUI = 0x0400; // 無錯誤 UI
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public uint wFunc;
[MarshalAs(UnmanagedType.LPWStr)] public string pFrom;
[MarshalAs(UnmanagedType.LPWStr)] public string pTo;
public ushort fFlags;
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)] public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);
///
/// 將檔案/目錄移至資源回收筒
///
public static bool DeleteToRecycleBin(string path)
{
if (!File.Exists(path) && !Directory.Exists(path)) return false;
var op = new SHFILEOPSTRUCT
{
hwnd = IntPtr.Zero,
wFunc = FO_DELETE,
pFrom = path + '\0' + '\0', // 雙 null 結尾
pTo = null,
fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI
};
int result = SHFileOperation(ref op);
return result == 0 && !op.fAnyOperationsAborted;
}
}
// 使用
ShellFileOp.DeleteToRecycleBin(@"C:\temp.txt");
關鍵細節:pFrom 必須以雙 null 結尾(path + '\0\0'),否則 API 會讀取到記憶體垃圾導致失敗。
| 情境 | 建議 |
|---|---|
| 大檔案讀寫 | 使用 FileStream + byte[] 緩衝,或 CopyToAsync |
| 頻繁追加寫入 | 保持 StreamWriter 開啟,避免反覆 Open/Close |
| 跨平台路徑 | 用 Path.Combine、Path.DirectorySeparatorChar |
| 編碼問題 | 明確指定 Encoding.UTF8,避免預設 ANSI 亂碼 |
掌握 System.IO 命名空間的核心類別(File、FileInfo、Directory、DirectoryInfo、StreamReader/Writer),並善用 using 確保資源釋放,即能應對 90% 的檔案操作需求。特殊場景(資源回收筒、檔案關聯、延伸屬性)再引入 P/Invoke 補足。