Rust 寫文件的 4 種方法

所有軟件都必須在某個時刻向文件系統寫入數據。對於用 Rust 編寫的程序也是如此。日誌消息需要持久化 (只要它們不只是寫入標準輸出),並且需要保存數據以供以後使用,否則就會發生其他情況。當然,寫數據與讀數據是相反的。

在 Rust 中,有多種方法可以處理將數據寫入文件的問題。其中一些方法比其他方法更容易,但都有它們的理由。有時你希望直接將文本寫入文件,有時你希望寫入某種二進制格式。無論你的用例是什麼,Rust 都能有一種方法來處理它。

在本文中,將介紹在 Rust 中編寫文件的常用方法。

1,使用 fs:write 一次將所有數據寫入文件

通常的情況是持久化內存中已有的數據。一次寫入所有數據,無論數據是字符串還是二進制格式都無關緊要。此外,如果文件還不存在,還可以創建一個文件。

這種方法的優點是:

這種方法也有一些缺點:

下面的代碼是一次將所有數據寫入文件的示例:

use std::fs;

fn write_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
    fs::write(path, data)?;
    Ok(())
}

fn write_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
    fs::write(path, data)?;
    Ok(())
}

2,使用 fs::File API 一次將所有數據寫入文件

上面的方法只是這個方法的一個方便的包裝,fs::File 對如何處理文件及其內容有了更多的控制。

這裏唯一的區別是沒有方便的底層將 string 轉換爲二進制。在這種情況下,就需要自己來轉換。

這種方法的優點:

它的缺點包括:

下面的代碼是這種方法的示例:

use std::{fs, io::Write};

fn write_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = fs::File::create(path)?;

    // 你需要自己處理轉換,可以嘗試一次寫入所有數據
    file.write_all(&data.as_bytes())?;

    // 或者儘量多寫,但需要自己處理剩下的字節
    let remaining = file.write(&data.as_bytes())?;

    if remaining > 0 {
      // 處理剩下的字節
    }

    Ok(())
}

fn write_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = fs::File::create(path)?;

    file.write_all(&data)?;

    let remaining = file.write(&data)?;

    if remaining > 0 {
    }

    Ok(())
}

3,向文件追加數據

如果要向現有文件追加數據,有一種方便的方法。使用 fs::OpenOptions,可以對文件的打開方式進行細粒度控制。

這種方法的優點:

這種方法的缺點:

下面的代碼是這種方法的示例:

use std::{fs, io::Write};

fn append_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = fs::OpenOptions::new().create(true).append(true).open(&path)?;

    // 你需要自己處理轉換,可以嘗試一次寫入所有數據
    file.write_all(&data.as_bytes())?;

    // 或者儘量多寫,但需要自己處理剩下的字節
    let remaining = file.write(&data.as_bytes())?;

    if remaining > 0 {
    }

    Ok(())
}

fn append_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = fs::OpenOptions::new().create(true).append(true).open(&path)?;

    file.write_all(&data)?;

    let remaining = file.write(&data)?;

    if remaining > 0 {
    }

    Ok(())
}

4,用 BufWriter 向文件寫入和追加數據

以前提出的所有方法都有一個共同點:它們總是立即寫入給它們的所有內容,並且沒有靈活性。有一種方法,在不影響程序性能的情況下一致地寫入大塊數據,可以使用 BufWriter。在內部,BufWriter 緩衝接收到的數據,並優化將數據寫入文件系統的方式。這種方法不需要很多新概念,甚至還使用了上面已經學習過的 api。

這種方法的優點:

這種方法的缺點:

下面的代碼是這種方法的示例:

use std::fs;
use std::io::{BufWriter, Write};

fn append_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
    let file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
    let mut file = BufWriter::new(file);

    file.write_all(&data.as_bytes())?;

    let remaining = file.write(&data.as_bytes())?;
    if remaining != 0 {

    }

    // 絕對明確的刷新buffwriter
    file.flush()?;

    Ok(())
}

fn append_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
    let file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
    let mut file = BufWriter::new(file);

    file.write_all(&data)?;

    let remaining = file.write(&data)?;
    if remaining != 0 {
    }

    file.flush()?;

    Ok(())
}

總結

在開發軟件時,經常需要將數據寫入文件。在本文中,介紹了用 Rust 寫文件的四種不同方法。

一次寫入所有數據是用盡可能少的代碼執行操作的好方法。此外,不必過於擔心數據的格式。不過,這種便利是有代價的。需要寫入的數據越多,內存就越大,而且現有文件會被覆蓋。

使用 File API 一次寫入所有數據是將所有數據轉儲到文件系統的另一種方法。與它的包裝器 API 不同,它在處理此過程方面提供了更大的靈活性。此外,處理大量數據的問題仍然存在,大量的數據意味着大量的內存。

使用 OpenOptions API 向文件追加數據帶來了比以前所有方法更大的靈活性。在這裏,不必擔心數據的大小。可以在處理文件時將數據附加到文件中,並且可以保持較低的內存配置。不過,這種方法需要進行一些微調。追加數據通常會導致系統調用,這些調用有時非常昂貴,因此仍有改進的空間。

最後,使用 BufWriter 提供了最大的靈活性,但它也是執行寫操作的一種相對底層的方法。BufWriter 本身至少會緩衝數據,但其有效性取決於使用的情況。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/VtXrnNcIb8gT0YUfcb9ZPQ