在軟體開發中,檔案處理是不可或缺的基礎技能。無論是建立設定檔、日誌記錄、資料匯出或是讀取外部資料,文字檔案操作都是程式設計師必須熟練掌握的技術。在 Pascal 語言中,文字檔案的處理機制相當完善與直覺,適合作為理解檔案操作概念的入門基礎。

在這篇文章中,玄貓將帶你深入瞭解 Pascal 中文字檔案的處理機制,從基本概念到實際應用,讓你能夠靈活運用這些知識在實際開發中。

Pascal 文字檔案的基本概念

在 Pascal 中,操作文字檔案需要經過幾個關鍵步驟:宣告檔案變數、關聯檔案名稱、開啟檔案進行讀寫操作,最後關閉檔案。這個流程雖然簡單,但每個步驟都有其重要性。

檔案變數與關聯

首先,我們需要宣告一個 TextFile 型態的變數,然後使用 AssignFile 將此變數與實際檔案名稱關聯:

var
  f: TextFile;
begin
  AssignFile(f, 'example.txt');
  // 後續操作
end;

這段程式碼宣告了一個名為 fTextFile 型態變數,並透過 AssignFile 將它與 ’example.txt’ 檔案關聯。值得注意的是,此操作僅建立變數與檔案名稱的關聯,並不會實際開啟或建立檔案。這類別似於建立一個「指向」特設定檔案的參考,真正的檔案操作發生在後續的開啟步驟。

檔案操作模式

在 Pascal 中,文字檔案有三種主要的開啟模式:

  1. 寫入模式 (Rewrite):建立新檔案或覆寫現有檔案
  2. 讀取模式 (Reset):開啟現有檔案以讀取內容
  3. 附加模式 (Append):開啟現有檔案以在尾端增加內容

一個重要的限制是文字檔案在 Pascal 中無法同時進行讀寫操作,必須選擇其中一種模式。

寫入文字檔案

當我們需要將資料寫入檔案時,首先需要使用 Rewrite 開啟檔案:

AssignFile(f, 'example.txt');
Rewrite(f);
// 現在可以寫入資料

Rewrite 操作會建立一個全新的空檔案。如果同名檔案已存在,則會被覆寫,原有內容將會遺失。這點在處理重要資料時要特別留意。

使用 Write 與 Writeln 函式

寫入檔案的主要函式有 WriteWriteln,它們的行為類別似於控制檯輸出,但第一個引數需要指設定檔案變數:

Procedure ZapFile;
var
  f: TextFile;
  x, y: integer;
  Ok: boolean;
begin
  AssignFile(f, 'example.txt');
  ReWrite(f);
  
  x := 10;
  y := 7;
  
  Writeln(f, x);          // 寫入 10 到第一行,遊標移至第二行
  Write(f, x+2);          // 寫入 12 到第二行,遊標保持在第二行末尾
  Write(f, 'Hello');      // 在第二行末尾寫入 "Hello"
  Write(f, x, y);         // 在遊標位置寫入 10 然後 7 (不含空格)
  Writeln(f, x, y);       // 寫入 10 然後 7,遊標移至第三行
  
  Ok := 5 > 7;
  Writeln(f, Ok);         // 寫入 False (Ok 的值)到第三行
  
  Writeln(f, x, ' ', y);  // 寫入 "10  7" 到第四行
  Writeln(f, 'x=', x);    // 寫入 "x=10" 到第五行
  
  CloseFile(f);
end;

這個範例展示了 WriteWriteln 的不同行為:

  • Write 寫入資料後遊標停留在同一行末尾
  • Writeln 寫入資料後會自動換行,遊標移至下一行開頭
  • 可以連續寫入不同型態的資料,包括整數、字串、布林值等
  • 多個引數之間不會自動加入空格,需要明確指定

最後的 CloseFile 非常重要!沒有正確關閉檔案可能導致資料遺失或檔案損壞,因為有些資料可能仍在緩衝區中未實際寫入磁碟。這是初學者容易忽略的關鍵步驟。

使用 Append 模式附加資料

有時我們不想覆寫檔案,而是要在現有檔案末尾增加內容,這時可以使用 Append 模式:

AssignFile(f, 'example.txt');
Append(f);
// 寫入的內容將增加到檔案末尾

使用 Append 時,遊標會自動定位在檔案末尾,所有寫入操作都會從該位置開始。需要注意的是,Append 只能用於已存在的檔案,若檔案不存在將導致執行錯誤。

封裝檔案寫入功能

在實際開發中,我常將檔案操作功能封裝成獨立程式,提高程式碼的可重用性。以下是一個實用範例,可以建立包含特定行列數字的檔案:

procedure ZapZero(var fl: TextFile; n, m: integer);
var
  i, j: integer;
begin
  Rewrite(fl);
  for i := 1 to n do
  begin
    for j := 1 to m do
      write(fl, 0:3);
    writeln(fl)
  end;
  CloseFile(fl)
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  f, g: TextFile;
begin
  AssignFile(f, 'test1.txt');
  AssignFile(g, 'test2.txt');
  ZapZero(f, 3, 5);  // 35列零
  ZapZero(g, 4, 3);  // 43列零
end;

這個範例展示瞭如何封裝檔案操作為可重用的程式:

  • ZapZero 接收檔案變數和兩個整數引數,分別表示行數和列數
  • 函式中使用巢狀迴圈產生指定格式的輸出,每個數字佔用3個字元寬度
  • 檔案變數以 var 關鍵字傳遞,這在 Pascal 中是必要的,因為檔案變數需要被修改
  • 函式內部負責開啟和關閉檔案,簡化了主程式的結構

這種封裝方式在處理多個相似檔案時特別有用,可以大幅減少重複程式碼。

讀取文字檔案

讀取檔案與寫入操作相似,但使用 Reset 函式開啟檔案:

AssignFile(f, 'example.txt');
Reset(f);
// 現在可以讀取資料

使用 Reset 時,檔案必須已存在,否則會產生執行時錯誤。開啟後,遊標會定位在檔案的第一行開頭。

基本讀取操作

最簡單的讀取操作是使用 ReadLn 讀取整行內容:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
begin
  AssignFile(f, 'my.txt');
  Reset(f);
  ReadLn(f, s);            // 讀取第一行
  memEx1.Lines.Append(s);  // 顯示在備忘錄元件中
  CloseFile(f);
end;

這段程式碼演示了基本的檔案讀取流程:

  1. 關聯檔案變數與檔名
  2. 以讀取模式開啟檔案
  3. 讀取一行內容到字串變數
  4. 將讀取的內容顯示在介面元件中
  5. 關閉檔案

每次執行 ReadLn 後,遊標會自動移至下一行的開頭。這是 Pascal 檔案操作的一個便利特性,簡化了逐行讀取的程式。

讀取多行內容

若要讀取多行內容,可以結合迴圈使用:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  i: integer;
begin
  AssignFile(f, 'my.txt');
  Reset(f);
  For i := 1 to 5 do
  begin
    ReadLn(f, s);
    memEx1.Lines.Append(s);
  end;
  CloseFile(f);
end;

這段程式碼會讀取檔案中的前五行內容。在實際應用中,我們需要注意檔案可能沒有足夠的行數,這種情況下程式會產生錯誤。因此,在生產環境中應該加入檢查機制,確保不會嘗試讀取超出檔案範圍的內容。

跳過特定行內容

如果我們只想讀取特定行,可以使用不帶變數的 ReadLn(f) 來跳過不需要的行:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  i: integer;
begin
  AssignFile(f, 'my.txt');
  Reset(f);
  For i := 1 to 4 do
    ReadLn(f);          // 跳過前4
  Readln(f, s);         // 讀取第5
  memEx1.Lines.Append(s);
  CloseFile(f);
end;

這段程式碼展示瞭如何跳過檔案中的特定行數。不帶變數的 ReadLn(f) 只會移動遊標到下一行,不讀取任何內容。這在只需要處理檔案特定部分時非常有用,可以提高效率。

需要注意的是,Pascal 中的文字檔案是循序存取的,無法直接跳到特定行,必須從頭開始逐行讀取。這是文字檔案處理的一個基本限制。

讀取未知行數的檔案

在實際應用中,我們通常不知道檔案有多少行。為了讀取整個檔案,可以使用 EOF 函式(End Of File):

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
begin
  AssignFile(f, 'my.txt');
  Reset(f);
  While not EOF(f) do
  begin
    ReadLn(f, s);
    memEx1.Lines.Append(s);
  end;
  CloseFile(f);
end;

這段程式碼使用 EOF(f) 函式檢查是否已到達檔案末尾。只要還沒到末尾,迴圈就會繼續執行,從而讀取檔案中的所有行。這是處理未知長度檔案的標準方法,也是我在實際開發中最常使用的方式。

重要的是,這裡必須使用 ReadLn 而非 Read。如果使用 Read,遊標不會自動移至下一行,可能導致無限迴圈,因為永遠不會達到檔案末尾。

檢測行末:EOLN 函式

除了檢測檔案末尾,有時我們需要知道是否到達了行末。Pascal 提供了 EOLN 函式(End Of Line)來檢測遊標是否位於行末:

if EOLN(f) then
  // 遊標位於行末

這在需要逐字元處理檔案內容時特別有用,可以幫助確定何時換行。

文字檔案處理的最佳實踐

在多年的開發經驗中,我總結了一些處理文字檔案的最

數值型文字檔案操作:Delphi高效讀取技巧

在前面的文章中,我們瞭解瞭如何逐行讀取文字檔案。今天將進一步探討如何從檔案中讀取以空格分隔的數值資料。這是處理資料檔案時非常實用的技巧,讓我們深入瞭解Delphi如何直接讀取數值型變數。

空格分隔數值的讀取機制

假設我們有一個文字檔案,其中包含由一個或多個空格分隔的數字。當我們使用Read(f, x)陳述式時(其中f是檔案變數,x是整數變數),系統會如何處理呢?

讓我以實際運作機制說明:

  1. 執行Read(f, x)後,x變數會設為檔案中的第一個數值
  2. 遊標會停在該數值後的分隔符(空格)上
  3. 再次執行Read(f, x)時,系統會跳過所有分隔符(空格),讀取下一個數值
  4. 遊標再次停在數值後的分隔符上

簡而言之,Read(f, x)對整數變數x的操作順序是:跳過數值前的所有分隔符 → 讀取數值到變數 → 將遊標停在數值後的非數字符號處。

實戰範例:讀取空格分隔的數值

讓我們透過一個例項來理解這個機制:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  x, i, k: integer;
begin
  AssignFile(f, 'my.txt'); // 關聯檔案名稱和檔案變數
  Reset(f);                // 開啟檔案以供讀取
  k := 0;
  For i := 1 to 5 do       // 執行五次:
  begin
    Read(f, x);            // 讀取下一個數字
    k := k + x;            // 將讀取的數字加到總和中
  end;
  CloseFile(f);            // 關閉檔案
end;

這段程式碼開啟名為’my.txt’的檔案,然後連續讀取五個數字並計算它們的總和。假設檔案內容為「10 7 25 14 1…」,那麼執行後k變數將包含57(即10+7+25+14+1)。注意Read(f, x)會自動跳過所有空格,直接讀取數值。這是處理以空格分隔的數值檔案的高效方法。

Read與ReadLn的關鍵差異

如果我們將上面範例中的Read(f, x)改為ReadLn(f, x)會發生什麼?這兩個函式有著關鍵性的差異:

  • Read讀取下一個數值,遊標停在數值後的分隔符上
  • ReadLn讀取當前行的下一個數值,然後將遊標移到下一行的開頭

讓我們修改範例看區別:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  x, i, k: integer;
begin
  AssignFile(f, 'my.txt'); // 關聯檔案名稱和檔案變數
  Reset(f);                // 開啟檔案以供讀取
  k := 0;
  For i := 1 to 5 do       // 執行五次:
  begin
    ReadLn(f, x);          // 讀取行首的數字並移至下一行
    k := k + x;            // 將讀取的數字加到總和中
  end;
  CloseFile(f);            // 關閉檔案
end;

這個版本使用ReadLn而非Read,它只會讀取每行的第一個數字,然後跳到下一行。假設檔案的前五行分別以10、14、24、27和11開頭,執行此程式後k變數將包含86(10+14+24+27+11)。這種讀取方式適合當每行只需讀取第一個數值的情境。

讀取一行中所有數值的挑戰

在實際應用中,我們經常需要讀取一行中的所有數字,但事先不知道數量。直覺上,我們可能會使用EOLN(f)函式來檢查行尾:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  x, k: integer;
begin
  AssignFile(f, 'my.txt'); // 關聯檔案名稱和檔案變數
  Reset(f);                // 開啟檔案以供讀取
  k := 0;
  While not EOLN(f) do     // 當未到行尾時迴圈
  begin
    Read(f, x);            // 讀取下一個數字
    k := k + x;            // 將讀取的數字加到總和中
  end;
  CloseFile(f);            // 關閉檔案
end;

這段程式碼看似能讀取第一行的所有數字(例如"10 7 25",總和為42),但實際上可能會出問題。如果最後一個數字後有空格,EOLN(f)會回傳False,因為空格不是行尾符號。這會導致程式繼續執行Read(f, x),跳過所有分隔符(包括行尾符號),直到讀到下一行的數字。結果會讀取多行資料,而非僅限於第一行。

SeekEOLN與SeekEOF:解決空格問題的關鍵函式

為瞭解決上述問題,Delphi提供了兩個特殊函式:

  • SeekEOLN(f):先跳過所有空格,然後檢查是否到達行尾
  • SeekEOF(f):跳過所有空格和行尾符號,然後檢查是否到達檔案尾

正確讀取一行中所有數值

使用SeekEOLN可以解決我們前面遇到的問題:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  x, k: integer;
begin
  AssignFile(f, 'my.txt'); // 關聯檔案名稱和檔案變數
  Reset(f);                // 開啟檔案以供讀取
  k := 0;
  While not SeekEOLN(f) do // 當未到行尾時迴圈(先跳過空格)
  begin
    Read(f, x);            // 讀取下一個數字
    k := k + x;            // 將讀取的數字加到總和中
  end;
  CloseFile(f);            // 關閉檔案
end;

這個版本使用SeekEOLN(f)代替EOLN(f),它會先跳過所有空格再檢查行尾,因此能正確處理數字後有空格的情況。不論行尾是否有空格,這段程式碼都能正確讀取第一行的所有數字。在我的實際開發中,這個細節常被忽略,但卻是處理數值檔案時的關鍵。

讀取整個檔案的所有數值

如果要讀取檔案中的所有數值,可以使用SeekEOF函式:

procedure TfrmEx1.btnEx1Click(Sender: TObject);
var
  f: TextFile;
  s: string;
  x, k: integer;
begin
  AssignFile(f, 'my.txt'); // 關聯檔案名稱和檔案變數
  Reset(f);                // 開啟檔案以供讀取
  k := 0;
  While not SeekEOF(f) do  // 當未到檔案尾時迴圈(跳過空格和行尾)
  begin
    Read(f, x);            // 讀取下一個數字
    k := k + x;            // 將讀取的數字加到總和中
  end;
  CloseFile(f);            // 關閉檔案
end;

這段程式碼使用SeekEOF(f),能夠讀取整個檔案中的所有數字並計算總和,不受行尾空格或換行符的影響。當處理多行數值資料時,這是最可靠的方法。從經驗來看,這種寫法在處理大型數值檔案時特別有用,它能確保不會漏讀任何數值。

檔案讀取的黃金法則

根據我多年的開發經驗,處理文字檔案讀取時,可以遵循以下規則:

  • 如果您以行為單位讀取資料,使用EOLN(f)EOF(f)函式
  • 如果您以數值為單位讀取資料,使用SeekEOLN(f)SeekEOF(f)函式

這個簡單的法則能幫助你避免許多檔案讀取中的常見陷阱。

錯誤處理注意事項

當程式嘗試從非數值資料(檔案尾或純文字)讀取數值時,執行時系統會顯示錯誤訊息並終止程式執行。以下情況也會導致程式中斷:

  • 嘗試將實數讀入整數變數
  • 檔案中的數字格式不符合Delphi格式(如使用逗號而非句點作為小數點分隔符)

在實際應用中,建議加入適當的錯誤處理機制,例如使用try-except結構來捕捉可能的讀取異常。

實用練習題:鞏固你的檔案處理技能

以下是一些實用練習題,可幫助你鞏固文字檔案處理技能:

練習1:尋找最短行

編寫一個程式,找出檔案中最短的一行並將其內容顯示在Label元件中。如有多個等長的最短行,顯示最後一個。檔案名稱應從文字框讀取。

練習2:迴文行檢測

編寫一個程式,讀取檔案中的每一行,並將迴文行輸出到TMemo元件中。檔案名稱應從文字框輸入(可使用對話方塊)。

練習3:偶數和行篩選

一個文字檔案包含多行,每行包含幾個由一個或多個空格分隔的整數。編寫一個程式,讀取該檔案並將只包含偶數和的行寫入另一個檔案。檔案名稱從文字框輸入。

練習4:奧林匹克參與者分析

一個文字檔案包含參加奧林匹克的兒童和學校的資訊。第一行包含兒童記錄的數量,隨後的每一行格式如下:

<姓氏> <縮寫> <學校編號>

其中<姓氏>是不超過20個符號的字串,<縮寫>是包含四個符號的字串(字元、句點、字元、句點),<學校編號>是一位或兩位數字。各項之間用單個空格分隔。

編寫一個程式,輸出參與者人數最少的學校編號。如果有多個學校擁有相同最少數量的參與者,應以逗號分隔列出所有學校編號。

這些練習題涵蓋了不同的檔案處理場景,從基本的行處理到複雜的資料分析,能幫助你全面掌握Delphi的檔案處理技術。

文字檔案處理的進階思考

在處理文字檔案時,我發現許多開發者容易忽略效能考量。當處理大型檔案時,讀取策略會直接影響程式的執行效率。在我的實踐中,對於需要重複處理的大型檔案,通常會考慮以下最佳化策略:

  1. 使用緩衝技術,一次讀取多行進行批次處理
  2. 對於格式已知的數值檔案,使用更高效的二進位讀取
  3. 實作讀取進度指示器,特別是處理時間可能較長的大檔案

這些進階技巧能顯著提升檔案處理效率,特別是在處理資料密集型應用時。

文字檔案操作是幾乎所有應用程式開發中都會遇到的基礎課題。掌握了本文介紹的技術,特別是理解了Read/ReadLn和

Delphi 標準檔案對話方塊:提升使用者經驗的檔案操作方式

在開發桌面應用程式時,提供友善的檔案選擇介面是提升使用者經驗的關鍵。雖然在前面我們已經學習了基本的檔案操作方法,但這些方法通常需要預先知道檔案路徑,對一般使用者來說並不直覺。今天玄貓要帶大家探討 Delphi 中的標準檔案對話方塊元件,這些元件能讓使用者透過熟悉的視窗介面選擇檔案,大幅提升應用程式的易用性。

標準檔案對話方塊元件概述

Delphi 提供了兩個主要的檔案對話方塊元件,位於工具箱的 Dialogs 標籤中:

  1. OpenDialog:用於選擇已存在的檔案進行讀取操作
  2. SaveDialog:用於選擇存檔位置,可以是已存在的檔案或新建檔案

這些元件放置於表單後,在程式執行時並不會直接顯示,而是需要透過程式碼觸發。與傳統檔案操作相比,這種方式更加直覺與符合現代桌面應用程式的操作習慣。

檔案對話方塊的核心屬性與方法

檔案對話方塊元件有兩個關鍵的元素:

  • FileName 屬性:存放使用者選擇的檔案完整路徑
  • Execute 方法:顯示對話方塊並回傳一個布林值,表示使用者是否完成了檔案選擇

當使用者在對話方塊中選擇檔案並點選「開啟」或「儲存」按鈕時,Execute 方法會回傳 True,同時 FileName 屬性將包含選設定檔案的完整路徑。如果使用者取消操作,Execute 將回傳 False。

使用檔案對話方塊的基本流程

使用檔案對話方塊進行檔案操作的基本流程如下:

var
  Okf1, Okf2: Boolean;
  StF1, StF2: string;
begin
  // 檢查是否選擇了讀取檔案
  Okf1 := OpenDialog1.Execute;
  if Okf1 then
  begin
    StF1 := OpenDialog1.FileName;
    AssignFile(f, StF1);
    Reset(f);
    // 處理檔案資訊:讀取、處理等
    CloseFile(f);
  end;
  
  // 檢查是否選擇了寫入檔案
  Okf2 := SaveDialog1.Execute;
  if Okf2 then
  begin
    StF2 := SaveDialog1.FileName;
    AssignFile(g, StF2);
    Rewrite(g);
    // 寫入資訊到檔案
    CloseFile(g);
  end;
end;

這個流程將傳統的檔案操作與標準對話方塊結合,讓使用者能夠直覺地選擇檔案,同時保持了 Delphi 檔案處理的基本結構。

實作 OpenDialog 讀取檔案

讓我們深入瞭解如何使用 OpenDialog 元件來讀取檔案。首先,我們需要在表單上放置一個 OpenDialog 元件,然後實作相關程式碼:

var
  f: TextFile;
  Okf1: Boolean;
  StF1: string;
  line: string;
begin
  // 顯示開啟檔案對話方塊
  Okf1 := OpenDialog1.Execute;
  
  // 如果使用者選擇了檔案
  if Okf1 then
  begin
    // 取得檔案名稱
    StF1 := OpenDialog1.FileName;
    
    // 指派檔案變數
    AssignFile(f, StF1);
    
    // 開啟檔案進行讀取
    Reset(f);
    
    // 讀取檔案內容
    while not Eof(f) do
    begin
      ReadLn(f, line);
      Memo1.Lines.Add(line);
    end;
    
    // 關閉檔案
    CloseFile(f);
  end;
end;

這段程式碼示範了使用 OpenDialog 讀取文字檔案的完整流程。當執行 OpenDialog1.Execute 時,系統會顯示標準的檔案開啟對話方塊,使用者可以瀏覽檔案系統並選擇要開啟的檔案。

如果使用者選擇了檔案(按下「開啟」按鈕),Execute 方法回傳 True,我們就從 OpenDialog1.FileName 屬性中取得完整的檔案路徑,並使用標準的檔案操作程式開啟檔案。在這個例子中,我們讀取檔案的每一行,並將內容增加到一個 Memo 元件中顯示。

值得注意的是,這種方式讓使用者可以自由選擇任何位置的檔案,而不需要預先知道檔案路徑,大提高了應用程式的靈活性。

實作 SaveDialog 儲存檔案

同樣地,我們可以使用 SaveDialog 元件來實作檔案的儲存功能:

var
  g: TextFile;
  Okf2: Boolean;
  StF2: string;
begin
  // 顯示儲存檔案對話方塊
  Okf2 := SaveDialog1.Execute;
  
  // 如果使用者選擇了儲存位置
  if Okf2 then
  begin
    // 取得檔案名稱
    StF2 := SaveDialog1.FileName;
    
    // 指派檔案變數
    AssignFile(g, StF2);
    
    // 開啟檔案進行寫入
    Rewrite(g);
    
    // 寫入 Memo 內容到檔案
    WriteLn(g, Memo1.Text);
    
    // 關閉檔案
    CloseFile(g);
    
    ShowMessage('檔案已成功儲存!');
  end;
end;

這段程式碼展示了 SaveDialog 的基本使用方式。當執行 SaveDialog1.Execute 時,系統會顯示標準的檔案儲存對話方塊。使用者可以選擇一個已存在的檔案進行覆寫,或者輸入一個新的檔案名稱。

如果使用者完成選擇(按下「儲存」按鈕),我們就從 SaveDialog1.FileName 屬性取得檔案路徑,然後使用 Rewrite 開啟檔案進行寫入。在這個例子中,我們將一個 Memo 元件的內容寫入到檔案中。

這種方式讓使用者可以自由選擇儲存位置,同時也提供了一個標準的介面來確認檔案覆寫操作,增強了應用程式的安全性。

增強檔案對話方塊的功能

標準的檔案對話方塊還有許多屬性可以用來增強其功能:

設定檔案過濾器

我們可以透過 Filter 屬性設定檔案對話方塊中顯示的檔案類別:

procedure TForm1.FormCreate(Sender: TObject);
begin
  OpenDialog1.Filter := 'Text files (*.txt)|*.txt|All files (*.*)|*.*';
  SaveDialog1.Filter := 'Text files (*.txt)|*.txt|All files (*.*)|*.*';
  
  // 設定預設選擇的過濾器索引
  OpenDialog1.FilterIndex := 1;
  SaveDialog1.FilterIndex := 1;
end;

這段程式碼設定了檔案對話方塊的過濾器,使其預設只顯示文字檔案(*.txt)。Filter 屬性的格式為 描述|過濾器|描述|過濾器...,可以設定多個過濾選項。FilterIndex 屬性則指定預設選擇哪一個過濾器,索引值從 1 開始。

這樣設定後,當使用者開啟檔案對話方塊時,會看到一個下拉選單,可以選擇「Text files (.txt)」或「All files (.)」,預設選擇的是「Text files (.txt)」。

設定初始目錄

我們可以透過 InitialDir 屬性設定檔案對話方塊的初始目錄:

OpenDialog1.InitialDir := ExtractFilePath(Application.ExeName);
SaveDialog1.InitialDir := GetMyDocumentsPath; // 自定義函式,取得「我的檔案」路徑

這裡我們設定 OpenDialog 的初始目錄為應用程式所在的資料夾,而 SaveDialog 的初始目錄為使用者的「我的檔案」資料夾。這樣可以讓檔案對話方塊在開啟時直接導航到最可能的位置,提高使用者效率。

ExtractFilePath(Application.ExeName) 是 Delphi 提供的函式,用於取得應用程式執行檔所在的路徑。而 GetMyDocumentsPath 則是一個自定義函式,用於取得「我的檔案」的路徑。

設定預設檔案名稱

對於 SaveDialog,我們可以設定一個預設的檔案名稱:

SaveDialog1.FileName := 'output.txt';

這行程式碼設定了 SaveDialog 的預設檔案名稱為 “output.txt”。當使用者開啟儲存對話方塊時,這個名稱會預先填入檔案名稱欄位中,使用者可以直接受或修改它。這對於提供一個有意義的預設名稱很有用,尤其是當檔案名稱應該反映其內容或生成日期時。

實用範例:檔案內容處理

現在讓我們來看一個更完整的範例,結合 OpenDialog 和 SaveDialog 來處理檔案內容:

procedure TForm1.btnProcessClick(Sender: TObject);
var
  inputFile, evenFile, oddFile: TextFile;
  line: string;
  okInput, okEven, okOdd: Boolean;
begin
  // 選擇輸入檔案
  okInput := OpenDialog1.Execute;
  if not okInput then Exit;
  
  // 選擇存放偶數長度字串的檔案
  SaveDialog1.Title := '儲存偶數長度字串';
  SaveDialog1.FileName := 'even.txt';
  okEven := SaveDialog1.Execute;
  if not okEven then Exit;
  
  // 選擇存放奇數長度字串的檔案
  SaveDialog1.Title := '儲存奇數長度字串';
  SaveDialog1.FileName := 'odd.txt';
  okOdd := SaveDialog1.Execute;
  if not okOdd then Exit;
  
  // 開啟所有檔案
  AssignFile(inputFile, OpenDialog1.FileName);
  AssignFile(evenFile, SaveDialog1.FileName);
  Reset(inputFile);
  Rewrite(evenFile);
  
  // 重新執行 SaveDialog 取得第二個檔案名稱
  AssignFile(oddFile, SaveDialog1.FileName);
  Rewrite(oddFile);
  
  // 處理檔案內容
  while not Eof(inputFile) do
  begin
    ReadLn(inputFile, line);
    
    // 判斷字串長度的奇偶性
    if Length(line) mod 2 = 0 then
      WriteLn(evenFile, line)  // 偶數長度
    else
      WriteLn(oddFile, line);  // 奇數長度
  end;
  
  // 關閉所有檔案
  CloseFile(inputFile);
  CloseFile(evenFile);
  CloseFile(oddFile);
  
  ShowMessage('處理完成!');
end;

這個範例展示瞭如何使用檔案對話方塊來處理檔案內容。程式首先讓使用者選擇一個輸入檔案,然後分別選擇兩個輸出檔案用於儲存不同的處理結果。

程式讀取輸入檔案的每一行,判斷其長度是奇數還是偶數,然後將其寫入相應的輸出檔案中。這個例子展示瞭如何在一個操作中使用多個檔案對話方塊,以及如何根據需要修改對話方塊的屬性(如標題和預設檔案名)。

注意我們如何透過設定 SaveDialog1.Title 屬性來提供更明確的提示,讓使用者瞭解每個對話方塊的用途。這種小細節可以大幅提升應用程式的使用者經驗。