Skip to content

Ch 21: BufRead

BufRead是提供缓冲读取能力的trait,基于Readtrait添加了面向行的读取功能。BufReader是实现BufRead的结构。

BufRead的主要优势:

  • 减少系统调用,提高读取效率
  • 提供行读取接口
  • 支持按行迭代
pub trait BufRead: Read {
fn read_line(&mut self, buf: &mut String) -> Result<usize>;
fn lines(&mut self) -> Lines<Self>;
fn split(self, byte: u8) -> Split<Self>;
// 其他方法...
}

逐行读取,包含换行符:

use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = b"hello\nworld\n";
let reader = BufReader::new(&data[..]);
let mut line = String::new();
// read_line 读取一行(包括换行符)
let bytes_read = reader.read_line(&mut line)?;
println!("读取了 {} 字节", bytes_read);
println!("内容: {:?}", line);
println!("包含换行符: {}", line.ends_with('\n'));
Ok(())
}

返回行的迭代器:

use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = b"line one\nline two\nline three\n";
let reader = BufReader::new(&data[..]);
// lines() 返回迭代器,自动去除换行符
for line in reader.lines() {
println!("行: {}", line?);
}
// 输出:
// 行: line one
// 行: line two
// 行: line three
Ok(())
}

按字节分割:

use std::io::{BufRead, BufReader, Split};
fn main() -> std::io::Result<()> {
let data = b"hello world rust";
let reader = BufReader::new(&data[..]);
// split 按空格分割
let mut split_iter = reader.split(b' ');
// 第一个元素
if let Some(Ok(token)) = split_iter.next() {
println!("第一个token: {}", String::from_utf8_lossy(&token));
}
// 收集剩余
let tokens: Vec<Result<Vec<u8>, _>> = split_iter.collect();
println!("剩余tokens: {:?}", tokens.len());
Ok(())
}
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = "name,age,city\nAlice,30,NYC\nBob,25,LA\n";
let reader = BufReader::new(data.as_bytes());
for line in reader.lines() {
let line = line?;
// 跳过表头
if line.starts_with("name") {
continue;
}
let fields: Vec<&str> = line.split(',').collect();
if fields.len() >= 3 {
println!("姓名: {}, 年龄: {}, 城市: {}",
fields[0], fields[1], fields[2]);
}
}
Ok(())
}
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = b"hello\nworld\n";
let reader = BufReader::new(&data[..]);
// read_line 保留换行符
let mut s1 = String::new();
reader.read_line(&mut s1)?;
println!("read_line: {:?}", s1); // "hello\n"
// lines 不保留换行符
// 注意:reader已经被read_line消耗了一部分
// 实际中不要混合使用
Ok(())
}

BufRead没有直接提供matches方法,但可以用split实现:

use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = b"error: file not found\ninfo: loading\nerror: out of memory\n";
let reader = BufReader::new(&data[..]);
for line in reader.lines() {
let line = line?;
if line.starts_with("error:") {
println!("错误: {}", &line[6..].trim());
}
}
Ok(())
}
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let data = b"hello";
let reader = BufReader::new(&data[..]);
let mut chars = Vec::new();
let mut buf = [0u8; 1];
while reader.read(&mut buf)? > 0 {
chars.push(buf[0] as char);
}
println!("字符: {:?}", chars);
Ok(())
}
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
// 创建测试文件
std::fs::write("test.txt", "line 1\nline 2\nline 3\n")?;
let file = File::open("test.txt")?;
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
// 清理
std::fs::remove_file("test.txt")?;
Ok(())
}

BufRead提供lines()但不提供直接的缓冲行访问。可以通过fill_buf和consume手动实现:

use std::io::{BufRead, BufReader, Result};
fn main() -> Result<()> {
let data = b"first line\nsecond line\nthird";
let mut reader = BufReader::new(&data[..]);
loop {
// fill_buf 返回当前缓冲区的引用
let buf = reader.fill_buf()?;
if buf.is_empty() {
break;
}
// 查找第一个换行符
if let Some(pos) = memchr::memchr(b'\n', buf) {
println!("行: {:?}", String::from_utf8_lossy(&buf[..pos]));
// consume 消费已读取的字节
reader.consume(pos + 1);
} else {
// 没有换行符,消费所有数据
println!("行(无换行): {:?}", String::from_utf8_lossy(buf));
reader.consume(buf.len());
}
}
Ok(())
}
use std::io::{self, BufRead};
fn main() -> io::Result<()> {
let stdin = io::stdin();
println!("输入一些文字,按Ctrl+D结束:");
for line in stdin.lock().lines() {
let line = line?;
if line.is_empty() {
continue;
}
println!("你输入了: {}", line);
}
Ok(())
}
类型说明
BufReader<T>带缓冲的读取器
std::io::stdin().lock()标准输入加锁后
io::Cursor<Vec<u8>>内存缓冲区

今天我们学习了BufRead trait:

  1. read_line - 读取一行,包含换行符
  2. lines - 返回行的迭代器,自动去除换行符
  3. split - 按字节分割,返回迭代器
  4. fill_buf - 获取当前缓冲区的引用
  5. consume - 消费已读取的字节

BufRead是处理文本输入的强大工具,特别适合逐行读取文本文件。