VB.NET File(檔案) 筆記(進階篇)
File 類別用來處理「單一檔案」的直接操作,例如讀取內容、寫入內容、追加文字、複製、移動、刪除與檢查是否存在。只要需求是針對某一個檔案本身動作,通常就會先想到 System.IO.File。
檔案處理的難點不只在方法名稱,而在流程順序。讀取前要確認檔案是否存在,寫入前要決定是否覆蓋,備份要避免檔名衝突,刪除前要確認目標,錯誤發生時也要能分辨是路徑錯誤、權限不足、檔案被占用,還是資料夾不存在。
先理解 File 類別在做什麼
File 類別:System.IO.File 提供一組共享方法,用來直接操作檔案。常見方法包括 Exists、ReadAllText、WriteAllText、AppendAllText、Copy、Move 與 Delete。
File 類別的核心觀念
- 處理單一檔案:File 的重點是檔案內容與檔案本身操作。
- 不用建立 File 物件:多數用法是直接寫
File.ReadAllText(...)。 - 路徑仍要另外處理:組合路徑時建議搭配
Path.Combine。 - 資料夾仍要另外處理:資料夾建立與列出檔案通常交給
Directory。 - 檔案資訊另有工具:大小、修改時間等資訊常用
FileInfo補足。
| 類別 | 負責範圍 | 常見用途 |
|---|---|---|
| File | 單一檔案操作。 | 讀寫、追加、複製、移動、刪除。 |
| Path | 路徑字串處理。 | 組合路徑、取副檔名、取檔名。 |
| Directory | 資料夾操作。 | 建立資料夾、列出檔案、確認資料夾是否存在。 |
| FileInfo | 檔案資訊與物件式操作。 | 讀取大小、建立時間、修改時間。 |
常用方法快速看
Imports System.IO
Imports System.Text
Dim exists As Boolean = File.Exists(filePath)
Dim text As String = File.ReadAllText(filePath, Encoding.UTF8)
File.WriteAllText(filePath, text, Encoding.UTF8)
File.AppendAllText(filePath, text & Environment.NewLine, Encoding.UTF8)
File.Copy(sourcePath, backupPath, False)
File.Move(oldPath, newPath)
File.Delete(filePath)
文字檔讀取與寫入
場景一:櫃台公告文字載入與儲存
這個範例把公告內容儲存在應用程式資料夾內。讀取前先檢查檔案是否存在,儲存時使用 WriteAllText 覆蓋目前公告內容。
需要的主控項
TextBoxNotice:公告內容,建議設定Multiline = True。ButtonLoadNotice:載入公告。ButtonSaveNotice:儲存公告。LabelNoticeStatus:顯示狀態。
範例程式碼
Imports System.IO
Imports System.Text
Public Class Form1
Private noticeFolder As String
Private noticeFile As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
noticeFolder = Path.Combine(Application.StartupPath, "NoticeData")
noticeFile = Path.Combine(noticeFolder, "CounterNotice.txt")
Directory.CreateDirectory(noticeFolder)
TextBoxNotice.Multiline = True
TextBoxNotice.ScrollBars = ScrollBars.Vertical
LabelNoticeStatus.Text = "公告檔案位置:" & noticeFile
End Sub
Private Sub ButtonLoadNotice_Click(sender As Object, e As EventArgs) Handles ButtonLoadNotice.Click
If Not File.Exists(noticeFile) Then
TextBoxNotice.Text = String.Empty
LabelNoticeStatus.Text = "尚未建立公告檔案"
Return
End If
TextBoxNotice.Text = File.ReadAllText(noticeFile, Encoding.UTF8)
LabelNoticeStatus.Text = "公告載入完成"
End Sub
Private Sub ButtonSaveNotice_Click(sender As Object, e As EventArgs) Handles ButtonSaveNotice.Click
File.WriteAllText(noticeFile, TextBoxNotice.Text, Encoding.UTF8)
LabelNoticeStatus.Text = "公告儲存完成"
End Sub
End Class
邏輯解析
Directory.CreateDirectory會確保資料夾存在。File.Exists可避免讀取不存在的檔案。WriteAllText會用目前文字覆蓋檔案原內容。- 文字檔讀寫建議明確指定
Encoding.UTF8。
場景二:設備巡檢日誌追加
日誌資料通常要保留每一次紀錄,不適合用覆蓋寫入。這個範例使用 AppendAllText 把新訊息加到檔案尾端。
需要的主控項
TextBoxLogMessage:輸入巡檢訊息。TextBoxLogHistory:顯示歷史紀錄,建議設定Multiline = True。ButtonAddLog:追加紀錄。ButtonReadLog:讀取紀錄。LabelLogStatus:顯示狀態。
範例程式碼
Imports System.IO
Imports System.Text
Public Class Form1
Private logFolder As String
Private logFile As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
logFolder = Path.Combine(Application.StartupPath, "InspectionLogs")
logFile = Path.Combine(logFolder, "device.log")
Directory.CreateDirectory(logFolder)
TextBoxLogHistory.Multiline = True
TextBoxLogHistory.ScrollBars = ScrollBars.Vertical
End Sub
Private Sub ButtonAddLog_Click(sender As Object, e As EventArgs) Handles ButtonAddLog.Click
Dim messageText As String = TextBoxLogMessage.Text.Trim()
If messageText = String.Empty Then
LabelLogStatus.Text = "巡檢訊息不可空白"
Return
End If
Dim logLine As String = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") & " | " & messageText
File.AppendAllText(logFile, logLine & Environment.NewLine, Encoding.UTF8)
TextBoxLogMessage.Clear()
LabelLogStatus.Text = "巡檢紀錄已追加"
End Sub
Private Sub ButtonReadLog_Click(sender As Object, e As EventArgs) Handles ButtonReadLog.Click
If Not File.Exists(logFile) Then
TextBoxLogHistory.Text = String.Empty
LabelLogStatus.Text = "尚未建立日誌檔"
Return
End If
TextBoxLogHistory.Text = File.ReadAllText(logFile, Encoding.UTF8)
LabelLogStatus.Text = "日誌讀取完成"
End Sub
End Class
邏輯解析
AppendAllText會在檔案尾端追加內容。Environment.NewLine讓每筆紀錄各自換行。- 日誌需要累積資料時,不應使用
WriteAllText直接覆蓋。
複製、移動、重新命名與刪除
| 方法 | 用途 | 注意事項 |
|---|---|---|
| File.Copy | 複製檔案到新位置。 | 第三個參數決定是否允許覆蓋。 |
| File.Move | 移動檔案或重新命名。 | 原始路徑的檔案會被移走。 |
| File.Delete | 刪除檔案。 | 執行前應確認目標是否正確。 |
| File.Exists | 檢查檔案是否存在。 | 常在開啟、刪除、備份前使用。 |
場景三:報表輸出前自動備份舊檔
儲存新報表前,若原本檔案已存在,可以先建立時間戳記備份,再寫入新內容。這樣可以避免不小心覆蓋舊資料後無法回復。
需要的主控項
TextBoxReportContent:輸入報表內容。ButtonSaveReport:儲存報表。LabelReportStatus:顯示結果。
範例程式碼
Imports System.IO
Imports System.Text
Public Class Form1
Private reportFolder As String
Private reportFile As String
Private backupFolder As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
reportFolder = Path.Combine(Application.StartupPath, "Reports")
backupFolder = Path.Combine(reportFolder, "Backup")
reportFile = Path.Combine(reportFolder, "DailyReport.txt")
Directory.CreateDirectory(reportFolder)
Directory.CreateDirectory(backupFolder)
End Sub
Private Sub ButtonSaveReport_Click(sender As Object, e As EventArgs) Handles ButtonSaveReport.Click
If File.Exists(reportFile) Then
Dim backupName As String = "DailyReport_" &
DateTime.Now.ToString("yyyyMMdd_HHmmss") &
".txt"
Dim backupPath As String = Path.Combine(backupFolder, backupName)
File.Copy(reportFile, backupPath, False)
End If
File.WriteAllText(reportFile, TextBoxReportContent.Text, Encoding.UTF8)
LabelReportStatus.Text = "報表儲存完成,舊檔已視情況備份"
End Sub
End Class
邏輯解析
- 覆蓋重要檔案前,先檢查舊檔是否存在。
- 備份檔名加入時間戳記,可避免備份互相覆蓋。
File.Copy(reportFile, backupPath, False)表示若備份檔已存在就不覆蓋。
場景四:草稿重新命名與移除
File.Move 可用來移動檔案,也可用來在同一個資料夾中重新命名檔案。刪除則使用 File.Delete,但應先確認檔案存在。
需要的主控項
TextBoxOldDraftName:輸入原草稿名稱,不含副檔名。TextBoxNewDraftName:輸入新草稿名稱,不含副檔名。ButtonRenameDraft:重新命名。ButtonDeleteDraft:刪除草稿。LabelDraftStatus:顯示狀態。
範例程式碼
Imports System.IO
Public Class Form1
Private draftFolder As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
draftFolder = Path.Combine(Application.StartupPath, "DraftFiles")
Directory.CreateDirectory(draftFolder)
End Sub
Private Sub ButtonRenameDraft_Click(sender As Object, e As EventArgs) Handles ButtonRenameDraft.Click
Dim oldName As String = CleanFileName(TextBoxOldDraftName.Text.Trim())
Dim newName As String = CleanFileName(TextBoxNewDraftName.Text.Trim())
If oldName = String.Empty OrElse newName = String.Empty Then
LabelDraftStatus.Text = "原檔名與新檔名不可空白"
Return
End If
Dim oldPath As String = Path.Combine(draftFolder, oldName & ".txt")
Dim newPath As String = Path.Combine(draftFolder, newName & ".txt")
If Not File.Exists(oldPath) Then
LabelDraftStatus.Text = "原始草稿不存在"
Return
End If
If File.Exists(newPath) Then
LabelDraftStatus.Text = "新檔名已存在"
Return
End If
File.Move(oldPath, newPath)
LabelDraftStatus.Text = "草稿重新命名完成"
End Sub
Private Sub ButtonDeleteDraft_Click(sender As Object, e As EventArgs) Handles ButtonDeleteDraft.Click
Dim draftName As String = CleanFileName(TextBoxOldDraftName.Text.Trim())
If draftName = String.Empty Then
LabelDraftStatus.Text = "請輸入要刪除的草稿名稱"
Return
End If
Dim targetPath As String = Path.Combine(draftFolder, draftName & ".txt")
If Not File.Exists(targetPath) Then
LabelDraftStatus.Text = "檔案不存在,無法刪除"
Return
End If
File.Delete(targetPath)
LabelDraftStatus.Text = "草稿已刪除"
End Sub
Private Function CleanFileName(source As String) As String
For Each badChar As Char In Path.GetInvalidFileNameChars()
source = source.Replace(badChar, "_"c)
Next
Return source
End Function
End Class
邏輯解析
Path.GetInvalidFileNameChars可用來整理不合法檔名字元。File.Move在同一資料夾中可達成重新命名效果。- 刪除前先檢查存在,可讓畫面顯示更明確的狀態。
例外處理與錯誤分類
| 例外類型 | 常見原因 | 處理方向 |
|---|---|---|
| FileNotFoundException | 指定檔案不存在。 | 確認路徑與檔名。 |
| DirectoryNotFoundException | 資料夾不存在。 | 先建立資料夾或修正路徑。 |
| UnauthorizedAccessException | 權限不足,或目標不是可寫檔案。 | 改用可寫位置或調整權限。 |
| IOException | 檔案被占用、磁碟 I/O 問題。 | 稍後重試或檢查是否被其他程式開啟。 |
| ArgumentException | 路徑字串格式不合法。 | 檢查空白、非法字元與路徑組合。 |
場景五:匯入文字檔的安全讀取流程
檔案讀取失敗時,不應只顯示「錯誤」。這個範例把常見錯誤分成不同類型,讓畫面能指出比較具體的處理方向。
需要的主控項
TextBoxImportPath:輸入要讀取的檔案路徑。TextBoxImportContent:顯示檔案內容,建議設定Multiline = True。ButtonReadImport:讀取檔案。LabelErrorType:顯示錯誤類型。LabelErrorMessage:顯示處理訊息。
範例程式碼
Imports System.IO
Imports System.Text
Public Class Form1
Private Sub ButtonReadImport_Click(sender As Object, e As EventArgs) Handles ButtonReadImport.Click
Dim filePath As String = TextBoxImportPath.Text.Trim()
LabelErrorType.Text = "狀態:讀取中"
LabelErrorMessage.Text = "訊息:--"
TextBoxImportContent.Text = String.Empty
Try
Dim content As String = File.ReadAllText(filePath, Encoding.UTF8)
TextBoxImportContent.Text = content
LabelErrorType.Text = "狀態:成功"
LabelErrorMessage.Text = "訊息:檔案讀取完成"
Catch ex As FileNotFoundException
LabelErrorType.Text = "錯誤類型:檔案不存在"
LabelErrorMessage.Text = "訊息:請確認檔名與路徑"
Catch ex As DirectoryNotFoundException
LabelErrorType.Text = "錯誤類型:資料夾不存在"
LabelErrorMessage.Text = "訊息:請先建立資料夾或修正路徑"
Catch ex As UnauthorizedAccessException
LabelErrorType.Text = "錯誤類型:權限不足"
LabelErrorMessage.Text = "訊息:請改用可讀取的位置"
Catch ex As IOException
LabelErrorType.Text = "錯誤類型:I/O 錯誤"
LabelErrorMessage.Text = "訊息:檔案可能正在被其他程式使用"
Catch ex As ArgumentException
LabelErrorType.Text = "錯誤類型:路徑格式錯誤"
LabelErrorMessage.Text = "訊息:請確認路徑內容是否合法"
End Try
End Sub
End Class
邏輯解析
- 不同例外代表不同失敗原因。
- 介面顯示應盡量轉成可理解的處理方向。
IOException常見於檔案被占用或 I/O 操作失敗。
綜合應用:文字備忘檔管理器
場景六:備忘檔儲存、開啟、備份與刪除
這個範例整合 File、Path、Directory 與 FileInfo。流程包含清單刷新、儲存、開啟、備份、刪除與顯示檔案資訊。
需要的主控項
TextBoxMemoName:輸入備忘檔名稱,不含副檔名。TextBoxMemoEditor:編輯內容,建議設定Multiline = True。ListBoxMemoFiles:顯示檔案清單。ButtonRefreshMemo:刷新清單。ButtonSaveMemo:儲存檔案。ButtonOpenMemo:開啟檔案。ButtonBackupMemo:備份檔案。ButtonDeleteMemo:刪除檔案。LabelMemoStatus:顯示狀態。LabelMemoInfo:顯示檔案資訊。
範例程式碼
Imports System.IO
Imports System.Text
Public Class Form1
Private memoFolder As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
memoFolder = Path.Combine(Application.StartupPath, "MemoFiles")
Directory.CreateDirectory(memoFolder)
TextBoxMemoEditor.Multiline = True
TextBoxMemoEditor.ScrollBars = ScrollBars.Vertical
RefreshMemoList()
LabelMemoStatus.Text = "備忘資料夾:" & memoFolder
End Sub
Private Sub ButtonRefreshMemo_Click(sender As Object, e As EventArgs) Handles ButtonRefreshMemo.Click
RefreshMemoList()
LabelMemoStatus.Text = "清單已刷新"
End Sub
Private Sub ButtonSaveMemo_Click(sender As Object, e As EventArgs) Handles ButtonSaveMemo.Click
Dim memoName As String = CleanFileName(TextBoxMemoName.Text.Trim())
If memoName = String.Empty Then
LabelMemoStatus.Text = "檔名不可空白"
Return
End If
Dim memoPath As String = Path.Combine(memoFolder, memoName & ".txt")
File.WriteAllText(memoPath, TextBoxMemoEditor.Text, Encoding.UTF8)
RefreshMemoList()
ShowMemoInfo(memoPath)
LabelMemoStatus.Text = "備忘檔已儲存"
End Sub
Private Sub ButtonOpenMemo_Click(sender As Object, e As EventArgs) Handles ButtonOpenMemo.Click
If ListBoxMemoFiles.SelectedItem Is Nothing Then
LabelMemoStatus.Text = "請先選擇備忘檔"
Return
End If
Dim memoPath As String = Path.Combine(memoFolder, ListBoxMemoFiles.SelectedItem.ToString())
TextBoxMemoEditor.Text = File.ReadAllText(memoPath, Encoding.UTF8)
TextBoxMemoName.Text = Path.GetFileNameWithoutExtension(memoPath)
ShowMemoInfo(memoPath)
LabelMemoStatus.Text = "備忘檔已開啟"
End Sub
Private Sub ButtonBackupMemo_Click(sender As Object, e As EventArgs) Handles ButtonBackupMemo.Click
If ListBoxMemoFiles.SelectedItem Is Nothing Then
LabelMemoStatus.Text = "請先選擇備忘檔"
Return
End If
Dim sourcePath As String = Path.Combine(memoFolder, ListBoxMemoFiles.SelectedItem.ToString())
Dim backupName As String = Path.GetFileNameWithoutExtension(sourcePath) &
"_backup_" &
DateTime.Now.ToString("yyyyMMdd_HHmmss") &
Path.GetExtension(sourcePath)
Dim backupPath As String = Path.Combine(memoFolder, backupName)
File.Copy(sourcePath, backupPath, False)
RefreshMemoList()
LabelMemoStatus.Text = "備忘檔已備份"
End Sub
Private Sub ButtonDeleteMemo_Click(sender As Object, e As EventArgs) Handles ButtonDeleteMemo.Click
If ListBoxMemoFiles.SelectedItem Is Nothing Then
LabelMemoStatus.Text = "請先選擇備忘檔"
Return
End If
Dim memoPath As String = Path.Combine(memoFolder, ListBoxMemoFiles.SelectedItem.ToString())
If File.Exists(memoPath) Then
File.Delete(memoPath)
End If
TextBoxMemoName.Clear()
TextBoxMemoEditor.Clear()
LabelMemoInfo.Text = "檔案資訊:--"
RefreshMemoList()
LabelMemoStatus.Text = "備忘檔已刪除"
End Sub
Private Sub RefreshMemoList()
ListBoxMemoFiles.Items.Clear()
For Each filePath As String In Directory.GetFiles(memoFolder, "*.txt")
ListBoxMemoFiles.Items.Add(Path.GetFileName(filePath))
Next
End Sub
Private Sub ShowMemoInfo(filePath As String)
If Not File.Exists(filePath) Then
LabelMemoInfo.Text = "檔案資訊:--"
Return
End If
Dim info As New FileInfo(filePath)
LabelMemoInfo.Text = "大小:" & info.Length.ToString("N0") &
" 位元組 / 修改時間:" &
info.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss")
End Sub
Private Function CleanFileName(source As String) As String
For Each badChar As Char In Path.GetInvalidFileNameChars()
source = source.Replace(badChar, "_"c)
Next
Return source
End Function
End Class
整合重點
File.WriteAllText負責儲存,File.ReadAllText負責開啟。File.Copy建立時間戳記備份。File.Delete刪除選取的備忘檔。Directory.GetFiles取得資料夾中的文字檔清單。FileInfo補足檔案大小與修改時間。
大型檔案與實務限制
一次讀完整檔案的限制:ReadAllText、ReadAllLines、ReadAllBytes 會把內容一次載入記憶體。小型設定檔、備忘檔、日誌摘要很方便;但非常大的檔案不適合這樣處理。
| 需求 | 可用方式 | 原因 |
|---|---|---|
| 小型文字檔 | ReadAllText / WriteAllText | 簡潔,適合一次讀寫。 |
| 日誌追加 | AppendAllText | 保留舊資料,將新資料加到尾端。 |
| 大型文字檔逐行處理 | StreamReader | 避免一次載入全部內容。 |
| 大量寫入 | StreamWriter | 適合分批寫入。 |
| 二進位檔 | ReadAllBytes / WriteAllBytes | 處理圖片、PDF、壓縮檔等位元組內容。 |
實務判斷與常見誤區
常見問題整理
- 直接串接路徑字串:建議用
Path.Combine,避免斜線與平台差異問題。 - 寫入前忘記建立資料夾:應先用
Directory.CreateDirectory確保資料夾存在。 - 不知道 WriteAllText 會覆蓋:若要保留舊內容,應使用
AppendAllText或先備份。 - 刪除前沒有確認目標:刪除流程應先確認檔案名稱、路徑與是否存在。
- 只顯示 ex.Message:介面應盡量轉成可理解的錯誤類型與處理方向。
- 大型檔案仍一次讀入:大量資料應考慮
StreamReader或StreamWriter。 - 未指定文字編碼:跨環境讀寫文字時,建議明確使用
Encoding.UTF8。
| 情境 | 建議做法 | 原因 |
|---|---|---|
| 設定檔儲存 | WriteAllText | 每次保存目前完整設定。 |
| 操作紀錄 | AppendAllText | 保留歷史,不覆蓋舊紀錄。 |
| 重要檔案覆蓋前 | 先 Copy 備份。 | 降低資料遺失風險。 |
| 檔案改名 | File.Move | 同一資料夾內移動可視為重新命名。 |
| 檔案屬性資訊 | FileInfo | 取得大小、修改時間等資訊較方便。 |
重點整理
File類別適合處理單一檔案的讀取、寫入、追加、複製、移動與刪除。- 組合路徑應搭配
Path.Combine,資料夾處理應搭配Directory。 - 文字檔讀寫建議明確指定
Encoding.UTF8。 WriteAllText會覆蓋原內容,AppendAllText會追加到檔案尾端。- 重要檔案覆蓋前應先備份,備份檔名可加入時間戳記。
File.Move可用於移動檔案,也可用於重新命名。- 例外處理應盡量分辨檔案不存在、資料夾不存在、權限不足與檔案被占用。
- 大型檔案不適合一次讀完整內容,應考慮串流方式處理。