Rust 文件与 IO
读写文件、`BufReader`、路径操作、以及标准输入输出。
一次性读写
use std::fs;
use std::io;
fn main() -> io::Result<()> {
fs::write("demo.txt", "第一行\n第二行\n")?;
let content = fs::read_to_string("demo.txt")?;
print!("{}", content);
println!("字节数 {}", content.len());
// 二进制
let bytes = fs::read("demo.txt")?;
println!("字节数组长度 {}", bytes.len());
Ok(())
}第一行
第二行
字节数 20
字节数组长度 20fn main() -> io::Result<()> is a common pattern: when main returns Result, an error prints Error: ... and exits non-zero. Cleaner than unwrap everywhere.
逐行读大文件
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn main() -> io::Result<()> {
let file = File::open("demo.txt")?;
let reader = BufReader::new(file);
for (i, line) in reader.lines().enumerate() {
let line = line?;
if line.contains("第二") {
println!("第 {} 行命中:{}", i + 1, line);
}
}
Ok(())
}第 2 行命中:第二行BufReader reads 8 KB at a time and lines() slices from it. Calling lines() on a raw File works but makes one syscall per line — an order of magnitude slower.
写入
use std::fs::OpenOptions;
use std::io::{self, Write};
fn main() -> io::Result<()> {
// 追加
let mut f = OpenOptions::new()
.create(true)
.append(true)
.open("log.txt")?;
writeln!(f, "新日志 1")?;
writeln!(f, "新日志 2")?;
// 带缓冲写大量数据
let mut buf = io::BufWriter::new(std::fs::File::create("big.txt")?);
for i in 0..3 {
writeln!(buf, "第 {} 行", i)?;
}
buf.flush()?; // 必须
Ok(())
}$ cat big.txt
第 0 行
第 1 行
第 2 行| Method | Behaviour |
|---|---|
File::create | 创建或截断 |
OpenOptions::append | 追加 |
OpenOptions::create_new | 已存在则报错 |
BufWriter::flush | 把缓冲区写出去 |
BufWriter tries to flush on Drop but discards the error. Call flush()? explicitly or a full-disk error vanishes silently.路径
use std::path::{Path, PathBuf};
fn main() {
let p = Path::new("/tmp/demo/a.txt");
println!("{:?}", p.file_name());
println!("{:?}", p.extension());
println!("{:?}", p.parent());
println!("{}", p.display());
// 拼接
let mut buf = PathBuf::from("/tmp");
buf.push("demo");
buf.push("b.txt");
println!("{}", buf.display());
// 判断
println!("{} {}", p.exists(), p.is_file());
}Some("a.txt")
Some("txt")
Some("/tmp/demo")
/tmp/demo/a.txt
/tmp/demo/b.txt
false falseNever build paths with string concatenation — Windows uses \, giving mixed separators like a/b\c. PathBuf::push handles the platform.
标准输入输出
use std::io::{self, Write};
fn main() {
print!("请输入名字:");
io::stdout().flush().unwrap(); // 保证提示先显示
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let name = input.trim();
println!("你好,{}", name);
}请输入名字:张三
你好,张三print! without a newline may stay buffered so the user sees no prompt. Interactive programs must flush() after print!.
错误处理
use std::fs;
use std::io::ErrorKind;
fn main() {
match fs::read_to_string("nope.txt") {
Ok(s) => println!("{}", s),
Err(e) if e.kind() == ErrorKind::NotFound => println!("文件不存在"),
Err(e) if e.kind() == ErrorKind::PermissionDenied => println!("没有权限"),
Err(e) => println!("其他错误:{}", e),
}
}文件不存在一次性读写
use std::fs;
use std::io;
fn main() -> io::Result<()> {
fs::write("demo.txt", "第一行\n第二行\n")?;
let content = fs::read_to_string("demo.txt")?;
print!("{}", content);
println!("字节数 {}", content.len());
// 二进制
let bytes = fs::read("demo.txt")?;
println!("字节数组长度 {}", bytes.len());
Ok(())
}第一行
第二行
字节数 20
字节数组长度 20fn main() -> io::Result<()> 是常见的写法:main 返回 Result 时,出错会打印 Error: ... 并以非零状态码退出。比到处 unwrap 干净。
逐行读大文件
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn main() -> io::Result<()> {
let file = File::open("demo.txt")?;
let reader = BufReader::new(file);
for (i, line) in reader.lines().enumerate() {
let line = line?;
if line.contains("第二") {
println!("第 {} 行命中:{}", i + 1, line);
}
}
Ok(())
}第 2 行命中:第二行BufReader 会一次读 8KB 到缓冲区,lines() 从缓冲区切行。直接对 File 调 lines() 也能用,但每行一次系统调用,慢一个数量级。
写入
use std::fs::OpenOptions;
use std::io::{self, Write};
fn main() -> io::Result<()> {
// 追加
let mut f = OpenOptions::new()
.create(true)
.append(true)
.open("log.txt")?;
writeln!(f, "新日志 1")?;
writeln!(f, "新日志 2")?;
// 带缓冲写大量数据
let mut buf = io::BufWriter::new(std::fs::File::create("big.txt")?);
for i in 0..3 {
writeln!(buf, "第 {} 行", i)?;
}
buf.flush()?; // 必须
Ok(())
}$ cat big.txt
第 0 行
第 1 行
第 2 行| 方法 | 行为 |
|---|---|
File::create | 创建或截断 |
OpenOptions::append | 追加 |
OpenOptions::create_new | 已存在则报错 |
BufWriter::flush | 把缓冲区写出去 |
BufWriter 在 Drop 时会尝试 flush,但错误被丢弃。要确保数据落盘必须显式 flush()?,否则磁盘满这类错误会静默消失。路径
use std::path::{Path, PathBuf};
fn main() {
let p = Path::new("/tmp/demo/a.txt");
println!("{:?}", p.file_name());
println!("{:?}", p.extension());
println!("{:?}", p.parent());
println!("{}", p.display());
// 拼接
let mut buf = PathBuf::from("/tmp");
buf.push("demo");
buf.push("b.txt");
println!("{}", buf.display());
// 判断
println!("{} {}", p.exists(), p.is_file());
}Some("a.txt")
Some("txt")
Some("/tmp/demo")
/tmp/demo/a.txt
/tmp/demo/b.txt
false false路径拼接不要用字符串加号——Windows 的分隔符是 \,手拼会出 a/b\c 这种混合路径。PathBuf::push 会按平台处理。
标准输入输出
use std::io::{self, Write};
fn main() {
print!("请输入名字:");
io::stdout().flush().unwrap(); // 保证提示先显示
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let name = input.trim();
println!("你好,{}", name);
}请输入名字:张三
你好,张三print! 不带换行时输出可能留在缓冲区,用户看不到提示。所以交互式程序在 print! 之后要 flush()。
错误处理
use std::fs;
use std::io::ErrorKind;
fn main() {
match fs::read_to_string("nope.txt") {
Ok(s) => println!("{}", s),
Err(e) if e.kind() == ErrorKind::NotFound => println!("文件不存在"),
Err(e) if e.kind() == ErrorKind::PermissionDenied => println!("没有权限"),
Err(e) => println!("其他错误:{}", e),
}
}文件不存在