讀取檔案

現在我們要加上能夠讀取 filename 中命令列引數指定的檔案功能。首先我們需要有個檔案範本能讓我們測試,要確保 minigrep 執行無誤的最佳檔案範本就是文字文件,其中由數行重複的單字組成少量文字。範例 12-3 Emily Dickinson 的詩就是不錯的選擇!在專案根目錄建立一個檔案叫做 poem.txt,然後輸入此詩 「I’m Nobody! Who are you?」

檔案名稱:poem.txt

I’m nobody! Who are you?
Are you nobody, too?
Then there’s a pair of us - don’t tell!
They’d banish us, you know.

How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!

範例 12-3:以 Emily Dickinson 的詩作為絕佳測試範本

有了這些文字,接著修改 src/main.rs 來加上讀取檔案的程式碼,如範例 12-4 所示。

檔案名稱:src/main.rs

use std::env;
use std::fs;

fn main() {
    // --省略--
    let args: Vec<String> = env::args().collect();

    let query = &args[1];
    let filename = &args[2];

    println!("搜尋 {}", query);
    println!("目標檔案為 {}", filename);

    let contents = fs::read_to_string(filename)
        .expect("讀取檔案時發生了錯誤");

    println!("文字內容:\n{}", contents);
}

範例 12-4:讀取第二個引數指定的檔案內容

首先,我們加上另一個 use 陳述式來將標準函式庫中的另一個相關部分引入:我們需要 std::fs 來處理檔案。

main 中,我們加上新的陳述式:fs::read_to_string 會接收 filename、開啟該檔案並回傳檔案內容的 Result<String>

在陳述式之後,我們再次加上暫時的 println! 陳述式來在讀取檔案之後,顯示 contents 的數值,讓我們能檢查程式目前運作無誤。

讓我們用任何字串作為第一個命令列引數(因為我們還沒實作搜尋的部分)並與 poem.txt 檔案作為第二個引數來執行此程式碼:

$ cargo run the poem.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep the poem.txt`
搜尋 the
目標檔案為 poem.txt
文字內容:
I’m nobody! Who are you?
Are you nobody, too?
Then there’s a pair of us - don’t tell!
They’d banish us, you know.

How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!

很好!程式碼有讀取並印出檔案內容。但此程式碼有些缺陷。main 函式負責太多事情了,通常如果每個函式都只負責一件事的話,函式才能清楚直白且易於維護。另一個問題是我們盡可能地處理錯誤。由於程式還很小,此缺陷不算什麼大問題,但隨著程式增長時,這會越來越難清楚地修正。在開發程式時盡早重構是很好的做法,因為重構少量的程式碼會比較簡單。接下來就讓我們開始吧。