Rust 寫文件的 4 種方法
所有軟件都必須在某個時刻向文件系統寫入數據。對於用 Rust 編寫的程序也是如此。日誌消息需要持久化 (只要它們不只是寫入標準輸出),並且需要保存數據以供以後使用,否則就會發生其他情況。當然,寫數據與讀數據是相反的。
在 Rust 中,有多種方法可以處理將數據寫入文件的問題。其中一些方法比其他方法更容易,但都有它們的理由。有時你希望直接將文本寫入文件,有時你希望寫入某種二進制格式。無論你的用例是什麼,Rust 都能有一種方法來處理它。
在本文中,將介紹在 Rust 中編寫文件的常用方法。
1,使用 fs:write 一次將所有數據寫入文件
通常的情況是持久化內存中已有的數據。一次寫入所有數據,無論數據是字符串還是二進制格式都無關緊要。此外,如果文件還不存在,還可以創建一個文件。
這種方法的優點是:
-
使用方便
-
它僅僅是一行代碼
-
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。
這種方法的優點:
-
可以完全控制文件的處理方式
-
它給你很大的靈活性
-
BufWriter 在底層優化對文件系統的訪問
這種方法的缺點:
-
使用起來可能有點困難
-
它需要更多的代碼來實現
下面的代碼是這種方法的示例:
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