2025年9月8日 星期一

29.VB.NET 檔案 (File) 筆記 (進階篇)

VB.NET File(檔案)筆記(進階篇)

VB.NET File(檔案) 筆記(進階篇)

File 類別用來處理「單一檔案」的直接操作,例如讀取內容、寫入內容、追加文字、複製、移動、刪除與檢查是否存在。只要需求是針對某一個檔案本身動作,通常就會先想到 System.IO.File

檔案處理的難點不只在方法名稱,而在流程順序。讀取前要確認檔案是否存在,寫入前要決定是否覆蓋,備份要避免檔名衝突,刪除前要確認目標,錯誤發生時也要能分辨是路徑錯誤、權限不足、檔案被占用,還是資料夾不存在。

先理解 File 類別在做什麼

File 類別:System.IO.File 提供一組共享方法,用來直接操作檔案。常見方法包括 ExistsReadAllTextWriteAllTextAppendAllTextCopyMoveDelete

File 類別的核心觀念

  • 處理單一檔案:File 的重點是檔案內容與檔案本身操作。
  • 不用建立 File 物件:多數用法是直接寫 File.ReadAllText(...)
  • 路徑仍要另外處理:組合路徑時建議搭配 Path.Combine
  • 資料夾仍要另外處理:資料夾建立與列出檔案通常交給 Directory
  • 檔案資訊另有工具:大小、修改時間等資訊常用 FileInfo 補足。
類別 負責範圍 常見用途
File單一檔案操作。讀寫、追加、複製、移動、刪除。
Path路徑字串處理。組合路徑、取副檔名、取檔名。
Directory資料夾操作。建立資料夾、列出檔案、確認資料夾是否存在。
FileInfo檔案資訊與物件式操作。讀取大小、建立時間、修改時間。

常用方法快速看

VB.NET
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:顯示狀態。
範例程式碼
VB.NET / Windows Forms
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:顯示狀態。
範例程式碼
VB.NET / Windows Forms
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
畫面輸出結果(讀取日誌)
2025/04/20 09:12:05 | 冷氣濾網已清潔 2025/04/20 09:18:40 | 影印機碳粉量正常
邏輯解析
  • AppendAllText 會在檔案尾端追加內容。
  • Environment.NewLine 讓每筆紀錄各自換行。
  • 日誌需要累積資料時,不應使用 WriteAllText 直接覆蓋。

複製、移動、重新命名與刪除

方法用途注意事項
File.Copy複製檔案到新位置。第三個參數決定是否允許覆蓋。
File.Move移動檔案或重新命名。原始路徑的檔案會被移走。
File.Delete刪除檔案。執行前應確認目標是否正確。
File.Exists檢查檔案是否存在。常在開啟、刪除、備份前使用。

場景三:報表輸出前自動備份舊檔

儲存新報表前,若原本檔案已存在,可以先建立時間戳記備份,再寫入新內容。這樣可以避免不小心覆蓋舊資料後無法回復。

需要的主控項
  • TextBoxReportContent:輸入報表內容。
  • ButtonSaveReport:儲存報表。
  • LabelReportStatus:顯示結果。
範例程式碼
VB.NET / Windows Forms
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:顯示狀態。
範例程式碼
VB.NET / Windows Forms
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:顯示處理訊息。
範例程式碼
VB.NET / Windows Forms
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 操作失敗。

綜合應用:文字備忘檔管理器

場景六:備忘檔儲存、開啟、備份與刪除

這個範例整合 FilePathDirectoryFileInfo。流程包含清單刷新、儲存、開啟、備份、刪除與顯示檔案資訊。

需要的主控項
  • TextBoxMemoName:輸入備忘檔名稱,不含副檔名。
  • TextBoxMemoEditor:編輯內容,建議設定 Multiline = True
  • ListBoxMemoFiles:顯示檔案清單。
  • ButtonRefreshMemo:刷新清單。
  • ButtonSaveMemo:儲存檔案。
  • ButtonOpenMemo:開啟檔案。
  • ButtonBackupMemo:備份檔案。
  • ButtonDeleteMemo:刪除檔案。
  • LabelMemoStatus:顯示狀態。
  • LabelMemoInfo:顯示檔案資訊。
範例程式碼
VB.NET / Windows Forms
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
畫面輸出結果(備份後)
備忘檔已備份 大小:1,248 位元組 / 修改時間:2025/04/20 15:05:30
整合重點
  • File.WriteAllText 負責儲存,File.ReadAllText 負責開啟。
  • File.Copy 建立時間戳記備份。
  • File.Delete 刪除選取的備忘檔。
  • Directory.GetFiles 取得資料夾中的文字檔清單。
  • FileInfo 補足檔案大小與修改時間。

大型檔案與實務限制

一次讀完整檔案的限制:ReadAllTextReadAllLinesReadAllBytes 會把內容一次載入記憶體。小型設定檔、備忘檔、日誌摘要很方便;但非常大的檔案不適合這樣處理。

需求可用方式原因
小型文字檔ReadAllText / WriteAllText簡潔,適合一次讀寫。
日誌追加AppendAllText保留舊資料,將新資料加到尾端。
大型文字檔逐行處理StreamReader避免一次載入全部內容。
大量寫入StreamWriter適合分批寫入。
二進位檔ReadAllBytes / WriteAllBytes處理圖片、PDF、壓縮檔等位元組內容。

實務判斷與常見誤區

常見問題整理

  • 直接串接路徑字串:建議用 Path.Combine,避免斜線與平台差異問題。
  • 寫入前忘記建立資料夾:應先用 Directory.CreateDirectory 確保資料夾存在。
  • 不知道 WriteAllText 會覆蓋:若要保留舊內容,應使用 AppendAllText 或先備份。
  • 刪除前沒有確認目標:刪除流程應先確認檔案名稱、路徑與是否存在。
  • 只顯示 ex.Message:介面應盡量轉成可理解的錯誤類型與處理方向。
  • 大型檔案仍一次讀入:大量資料應考慮 StreamReaderStreamWriter
  • 未指定文字編碼:跨環境讀寫文字時,建議明確使用 Encoding.UTF8
情境建議做法原因
設定檔儲存WriteAllText每次保存目前完整設定。
操作紀錄AppendAllText保留歷史,不覆蓋舊紀錄。
重要檔案覆蓋前Copy 備份。降低資料遺失風險。
檔案改名File.Move同一資料夾內移動可視為重新命名。
檔案屬性資訊FileInfo取得大小、修改時間等資訊較方便。

重點整理

  1. File 類別適合處理單一檔案的讀取、寫入、追加、複製、移動與刪除。
  2. 組合路徑應搭配 Path.Combine,資料夾處理應搭配 Directory
  3. 文字檔讀寫建議明確指定 Encoding.UTF8
  4. WriteAllText 會覆蓋原內容,AppendAllText 會追加到檔案尾端。
  5. 重要檔案覆蓋前應先備份,備份檔名可加入時間戳記。
  6. File.Move 可用於移動檔案,也可用於重新命名。
  7. 例外處理應盡量分辨檔案不存在、資料夾不存在、權限不足與檔案被占用。
  8. 大型檔案不適合一次讀完整內容,應考慮串流方式處理。