Хотя решение, на котором вы остановились, действительно работает, у него есть несколько недостатков. Во-первых, когда вы читаете из zip-файла, вам нужно прочитать содержимое файла, который вы хотите обработать, в память, прежде чем продолжить, что может быть непрактично для большого файла. Другой заключается в том, что в любом случае вам нужно выделить кучу BufReader
.
Еще одно, возможно, более идиоматическое решение c - это реструктурировать ваш код, чтобы BufReader
не нужно было возвращать из функция вообще - скорее, структурируйте свой код так, чтобы в нем была функция, которая открывает файл, которая, в свою очередь, вызывает функцию, обрабатывающую файл:
use std::ffi::OsStr;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;
pub fn process_file(filename: &str) -> Result<usize, String> {
let path = Path::new(filename);
let file = match File::open(&path) {
Ok(file) => file,
Err(why) => return Err(format!(
"ERROR: Could not open file, {}: {}",
path.display(),
why.to_string()
)),
};
if path.extension() == Some(OsStr::new("zip")) {
// Handling a zip file
let mut archive_contents=zip::ZipArchive::new(file).unwrap();
let mut buf_reader = BufReader::with_capacity(128 * 1024,archive_contents.by_index(0).unwrap());
process_reader(&mut buf_reader)
} else {
// Handling a plain file.
process_reader(&mut BufReader::with_capacity(128 * 1024, file))
}
}
pub fn process_reader(reader: &mut dyn BufRead) -> Result<usize, String> {
// Example, just count the number of lines
return Ok(reader.lines().count());
}
fn main() {
let mut files: Vec<String> = Vec::new();
files.push("/tmp/text_file.txt".to_string());
files.push("/tmp/zip_file.zip".to_string());
for f in files {
match process_file(&f) {
Ok(count) => println!("File {} Count: {}", &f, count),
Err(e) => println!("Error reading file: {}", e),
};
}
}
Таким образом, вам ничего не нужно Box
es, и вам не нужно читать файл в память перед его обработкой.
Недостатком этого решения было бы, если бы у вас было несколько функций, которые должны иметь возможность читать из zip-файлов. Один из способов справиться с этим - определить process_file
, который будет использовать функцию обратного вызова для выполнения обработки. Сначала вы измените определение process_file
на:
pub fn process_file<C>(filename: &str, process_reader: C) -> Result<usize, String>
where C: FnOnce(&mut dyn BufRead)->Result<usize, String>
Остальную часть тела функции можно оставить без изменений. Теперь process_reader
можно передать в функцию, например:
process_file(&f, count_lines)
где count_lines
будет исходной простой функцией, например, для подсчета строк.
Это будет также позволяют передать закрытие:
process_file(&f, |reader| Ok(reader.lines().count()))