我尝试以足够高效的方式阅读文件以达到我的目的。我有一个文件ID列表,名称和行索引(有序),每对(file_id, file_name, line_index)
我需要打开文件,按索引查找行并打印。
为了更高效(我知道输入是有序的)我想缓存逐行读取的BufReader
并让文件保持打开状态。
fn main() {
// positions in file
// structure: (file_id, file_name, line_index_in_file)
let positions = &vec![
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &Vec<(i32, String, i32)>) {
let mut last_file_id = -1;
//let mut last_file_name = None;
let mut open_file = None;
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
if let Some(to_close) = open_file {
drop(open_reader.unwrap());
drop(to_close);
}
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_file = Some(file);
open_reader = Some(BufReader::new(&file));
}
// use reader to find the line in file and process
}
}
我正面临这个问题:
main.rs:40:48: 40:52 error: `file` does not live long enough
main.rs:40 open_reader = Some(BufReader::new(&file));
main.rs:40:48: 40:52 error: use of moved value: `file` [E0382]
main.rs:40 open_reader = Some(BufReader::new(&file));
很明显(file
的生命周期真的很短),但我不知道如何解决它。
BufReader
取决于File
,但我需要在File
更改后稍后关闭file_id
。
另外,我觉得在循环中以这种方式调用drop
感觉不太舒服,因为它看起来像我试图欺骗编译器。那方法好吗?
请即使您知道更好的解决方案(例如,如何通过BufReader
关闭文件,我也很感激如何解决这个问题。)
答案 0 :(得分:3)
您可以将值File
传递给BufReader
。这样,您只拥有一个拥有文件句柄的变量。您可以使用take
上的Option
将内部值移出其中并留下None
。通过这种方式,您可以确保在下一个文件句柄被释放之前释放文件句柄(因此,如果您重新打开相同的文件,它就不会出现恐慌)
let mut open_reader = None;
for &(file_id, ref file_name, pos_in_file) in found {
println!("{} {}", file_id, pos_in_file);
if last_file_id < file_id {
last_file_id = file_id;
//last_file_name = file_ids.get(&file_id);
// take the value out of the `open_reader` to make sure that
// the file is closed, so we don't panic if the next statement
// tries to open the same file again.
open_reader.take();
//let file = File::open(last_file_name.unwrap()).unwrap();
let file = File::open(file_name).unwrap();
open_reader = Some(BufReader::new(file));
}
// use reader to find the line in file and process
}
答案 1 :(得分:2)
您已将该文件的所有权归属于BufReader
(这是显而易见的,因为它是按价值传递的),而不是借给它 - 它现在是{{1}关闭文件的工作。当它被删除时,它所拥有的BufReader
将被依次删除;所以你可以完全失去File
。
编译器成功阻止您在open_file
脚下破坏文件。
答案 2 :(得分:2)
我想缓存逐行读取的
BufReader
,并尽可能让文件保持打开状态。
最简单的方法是提前对数据进行分组:
use std::collections::HashMap;
fn print_lines_from_file(found: &[(i32, String, i32)]) {
let mut index = HashMap::new();
for line in found {
let name = &line.1;
index.entry(name).or_insert_with(Vec::new).push(line);
}
for (file_name, lines) in &index {
let file = File::open(file_name).unwrap();
for &&(file_id, _, line_index) in lines {
// do something with `file`
println!("processing ID {} ({}) line {}", file_id, file_name, line_index);
}
}
}
请注意,这使您不必拥有file_id
的特殊标记值(也可以使用Option
完成)。此外,即使您说数据已排序,这也允许您处理file_id
不是的情况。您还可以通过在矢量完成后对矢量进行排序来处理未排序的line_index
es的情况。
此外:
main
中有双重引用 - 您无需说&vec![...]
。&[T]
而不是&Vec<T>
。一个更美丽的解决方案,恕我直言,是使用itertools,特别是group_by_lazy
:
extern crate itertools;
use itertools::Itertools;
use std::fs::File;
use std::io::BufReader;
fn main() {
// structure: (file_id, file_name, line_index_in_file)
let positions = [
(1, String::from("file1"), 1),
(1, String::from("file1"), 2),
(1, String::from("file1"), 20),
(2, String::from("file2"), 15)
];
print_lines_from_file(&positions);
}
fn print_lines_from_file(found: &[(i32, String, i32)]) {
for (filename, positions) in &found.iter().group_by_lazy(|pos| &pos.1) {
println!("Opening file {}", filename);
// let file = File::open(file_name).expect("Failed to open the file");
// let file = BufReader::new(file);
for &(id, _, line) in positions {
println!("Processing ID {}, line {}", id, line);
}
}
}