Read large files line by line in Rust [duplicate]
Solution 1:
You want to use the buffered reader, BufRead
, and specifically the function BufReader.lines()
:
use std::fs::File;
use std::io::{self, prelude::*, BufReader};
fn main() -> io::Result<()> {
let file = File::open("foo.txt")?;
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
Ok(())
}
Note that you are not returned the linefeed, as said in the documentation.
If you do not want to allocate a string for each line, here is an example to reuse the same buffer:
fn main() -> std::io::Result<()> {
let mut reader = my_reader::BufReader::open("Cargo.toml")?;
let mut buffer = String::new();
while let Some(line) = reader.read_line(&mut buffer) {
println!("{}", line?.trim());
}
Ok(())
}
mod my_reader {
use std::{
fs::File,
io::{self, prelude::*},
};
pub struct BufReader {
reader: io::BufReader<File>,
}
impl BufReader {
pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
let file = File::open(path)?;
let reader = io::BufReader::new(file);
Ok(Self { reader })
}
pub fn read_line<'buf>(
&mut self,
buffer: &'buf mut String,
) -> Option<io::Result<&'buf mut String>> {
buffer.clear();
self.reader
.read_line(buffer)
.map(|u| if u == 0 { None } else { Some(buffer) })
.transpose()
}
}
}
Playground
Or if you prefer a standard iterator, you can use this Rc
trick I shamelessly took from Reddit:
fn main() -> std::io::Result<()> {
for line in my_reader::BufReader::open("Cargo.toml")? {
println!("{}", line?.trim());
}
Ok(())
}
mod my_reader {
use std::{
fs::File,
io::{self, prelude::*},
rc::Rc,
};
pub struct BufReader {
reader: io::BufReader<File>,
buf: Rc<String>,
}
fn new_buf() -> Rc<String> {
Rc::new(String::with_capacity(1024)) // Tweakable capacity
}
impl BufReader {
pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
let file = File::open(path)?;
let reader = io::BufReader::new(file);
let buf = new_buf();
Ok(Self { reader, buf })
}
}
impl Iterator for BufReader {
type Item = io::Result<Rc<String>>;
fn next(&mut self) -> Option<Self::Item> {
let buf = match Rc::get_mut(&mut self.buf) {
Some(buf) => {
buf.clear();
buf
}
None => {
self.buf = new_buf();
Rc::make_mut(&mut self.buf)
}
};
self.reader
.read_line(buf)
.map(|u| if u == 0 { None } else { Some(Rc::clone(&self.buf)) })
.transpose()
}
}
}
Playground