Rust 讀取文件的 5 種方法
讀取文件是在軟件開發中遇到的最常見的操作之一。加載配置文件、處理文件等通常是構建的軟件用例的一部分。
與其他編程語言一樣,在 Rust 中有多種讀取文件的方法。然而,這些方法都有其優點和缺點,理解在哪種情況下使用哪種方法是至關重要的。
在本文中,你將瞭解 Rust 最常用的讀取文件的方法。
1,將整個文件讀入到字符串
這種方法除了處理文件和處理其內容之外,不需要擔心任何事情。將整個文件讀入 String 的優點:
-
可以處理包含字符串內容的文件
-
可以一次整體處理
另一方面,這種方法也有它的缺點:
-
過大的文件可能會對性能產生嚴重影響
-
文件越大,程序的內存消耗就越大
-
包含二進制內容的文件不能以這種方式處理
下面的例子展示瞭如何將整個文件讀入 String:
use std::fs;
fn read_file_content_as_string(path: &str) -> Result<String, Box<dyn std::error::Error>> {
let string_content = fs::read_to_string(path)?;
Ok(string_content)
}
2,將整個文件讀入到字節向量
如果不處理 String 內容,但需要處理某種形式的二進制格式,則可以將整個文件讀入字節向量。不過,這個方法仍然適用於字符串內容。你必須自己實例化它,而不是直接從方法調用中接收 String。如果你不處理字符串內容,則不需要這樣做。
這個方法的優點是:
-
可以處理包含任何形式內容的文件
-
可以一次處理整個文件
缺點是:
-
文件太大可能會對性能產生嚴重影響
-
文件越大,程序的內存消耗就越大
下面的例子演示瞭如何將整個文件讀入字節向量:
use std::fs;
fn read_file_as_bytes(path: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let byte_content = fs::read(path)?;
Ok(byte_content)
}
如果將字節向量轉換爲 String,可以這樣做:
use std::fs;
use std::str;
fn read_file_as_bytes(path: &str) -> Result<String, Box<dyn std::error::Error>> {
let byte_content = fs::read(path)?;
let string_content = str::from_utf8(&byte_content)?;
Ok(string_content.to_string())
}
3,逐行讀取文件
如上所述,如果處理大文件,一次讀取整個文件可能會導致問題。在這種情況下,最好使用逐行方法處理這些文件。當然,這主要適用於具有 String 內容的文件。
Rust 在其標準庫中有一個方便的結構體,它去掉了一些較低級別的細節,稱爲 BufReader。這種方法可以處理以下特點的文件:
-
包含字符串內容的文件
-
不能一次處理太大的文件
然而,這種方法也有一些缺點:
-
它只適用於字符串內容的文件
-
實現可能很快變得更加複雜
-
根據文件的格式,如果不是要處理的所有內容都放在同一行,則可能需要自己緩衝行
下面的示例展示瞭如何逐行讀取文件:
use std::fs::File;
use std::io::{BufReader, BufRead};
fn read_file_line_by_line(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
for line in reader.lines() {
match line {
// line是字符串
Ok(line) => process_line(line),
Err(err) => handle_error(err),
}
}
Ok(())
}
4,以單個字節逐步讀取文件
前一種方法是逐行讀取文件,而將要介紹的這種方法允許你從 BufReader 處理的文件中讀取單個字節。
使用這種方法你需要:
-
需要完全控制文件內容的處理
-
自己實現大量的內容處理
-
自己處理緩衝,如果一次讀取所有文件內容會使內存消耗爆炸
它的缺點包括:
-
你必須處理原始數據。在這種情況下,它甚至是單個原始字節
-
你可能仍然需要一個緩衝區來臨時保存單個字節,直到可以將多個字節合併爲更有意義的內容
下面的例子演示瞭如何以單個字節逐步讀取文件:
use std::fs::File;
use std::io::{BufReader, Read};
fn read_file_as_single_bytes(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
for byte in reader.bytes() {
match byte {
// byte正好是一個字節
Ok(byte) => process_byte(byte),
Err(err) => handle_error(err),
}
}
Ok(())
}
5,以字節塊讀取文件
如果需要更大的靈活性,可以使用 BufReader 從文件中讀取塊。說實話,BufReader 也在底層進行了優化,當使用它的. bytes() 方法時,它不會單獨讀取每個字節。它以塊的形式讀取它們,然後從 Iterator 返回單個字節。
但是,當你想要自己處理塊時,這並沒有多大幫助。當然,也可以在使用 bytes() 時手動緩衝字節。
像其他方法一樣,以字節塊的形式讀取文件內容既有優點也有缺點。它的優點是:
-
可以完全控制如何處理文件的內容
-
提供了最大的靈活性,因爲可以動態調整塊大小並對特定情況做出反應
-
如果必須處理大文件,讀取所有文件內容將使內存消耗爆炸,則可以使用這種方法。
當然,這種方法也存在一些已知的缺陷:
-
必須處理原始數據,所有的解碼和處理都由你來決定
-
針對特定場景,可能需要進行幾次嘗試來優化緩衝區大小
-
如果塊太小,實際上可能會損害程序的整體性能 (太多的系統調用)。
下面的例子展示瞭如何以字節塊的形式讀取文件:
use std::fs::File;
use std::io::{BufReader, BufRead}
const BUFFER_SIZE: usize = 512;
fn read_file_in_byte_chunks(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = File::open(path)?;
let mut reader = BufReader::with_capacity(BUFFER_SIZE, file);
loop {
let buffer = reader.fill_buf()?;
let buffer_length = buffer.len();
if buffer_length == 0 {
break;
}
do_something_with(buffer);
// 衝緩衝區中消耗所有字節
reader.consume(buffer_length);
}
Ok(())
}
總結
讀取文件是開發軟件時常見的操作,本文介紹了在 Rust 中讀取文件 (包括字符串和原始二進制格式) 的五種常用方法。所有方法都有優點和缺點,需要選擇適合你的特定情況和用例的方法。
如果是小文件並處理 String 內容,將整個文件讀入 String 是一個很好的選擇。另一方面,如果文件變大或者根本不處理 String 內容,則該方法不是最好的。
如果文件很小,並且要處理任意的原始內容,那麼將整個文件讀入字節向量是一個不錯的選擇。但是,如果文件變大並且有內存限制,則不能使用此功能。
如果處理 String 內容並且不希望內存增長太多,那麼逐行讀取文件是一個很好的選擇。如果不處理 String 內容,並且文件將想要的內容分散到多行,那麼該方法就不夠用了,這需要你自己緩衝行。
以單個字節逐步讀取文件是最基本的方法之一。如果你想要靈活性和大量的控制,這是一個很好的選擇。另一方面,如果需要將多個字節合併爲更有意義的內容,可能還要自己進行數據緩衝。
最後,以字節塊讀取文件比單獨讀取每個字節要靈活一些。它提供了對數據處理的完全控制,也可以動態調整。但同樣,需要處理原始數據,並且可能需要一些時間來微調分塊。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/klp8pJBkj4tfD91ldZAx8Q