外观
第 8 节:文件系统 fs 与 Path
std::fs 管"文件操作",std::path::Path 管"路径操作"——两个是黄金搭档。
读写文件:三件套 ⭐
rust
use std::fs;
fs::write("demo.txt", "你好,Rust!")?; // 写入(覆盖)
let text = fs::read_to_string("demo.txt")?; // 读成文字(UTF-8)
let bytes = fs::read("demo.txt")?; // 读成字节(任何文件)read_to_string:文本文件(要求 UTF-8,读二进制会报错)read:任意文件的原始字节write:一步写入(覆盖整个文件;要追加用OpenOptions::new().append(true))
这三个都返回 Result,错误处理篇的主角(文件不存在、权限不够,都是日常)。
Path:路径的"拼接与问答" ⭐
rust
use std::path::Path;
let base = Path::new("data");
let file_path = base.join("logs").join("today.log");
// data/logs/today.log(Windows 下自动用 \)
file_path.is_file(); // 是文件吗?
base.is_dir(); // 是目录吗?
file_path.exists(); // 存在吗?
file_path.file_name(); // Some("today.log")
file_path.extension(); // Some("log")
file_path.parent(); // Some("data/logs")关键点:
join是拼路径的正道,别用format!("{}/{}.txt", dir, name)——join自动处理分隔符(Windows 的\和别的系统的/)file_name/extension/parent都返回Option(路径可能没有这些部分)- 判断文件类型用
extension:.ends_with(".csv")也行,但extension()更精确
创建目录 ⭐
rust
fs::create_dir("data")?; // 只建一层,已存在会报错
fs::create_dir_all("data/logs/2026")?; // 一路建到底,已存在不报错create_dir_all 是日常主角(创建多级目录、mkdir -p 的等价物)。
遍历目录:read_dir ⭐
rust
for entry in fs::read_dir("data/logs")? {
let entry = entry?;
let path = entry.path();
if path.extension().is_some_and(|e| e == "log") {
println!("找到日志:{}", path.display());
}
}read_dir返回目录项的迭代器,每一项entry也是Result(逐个处理,别整批 unwrap)- 只想处理"文件"不想要"子目录":用
entry.file_type()?.is_file()判断(本示例用扩展名过滤)
重命名与删除 ⭐
rust
fs::rename("a.txt", "b.txt")?; // 重命名/移动
fs::remove_file("b.txt")?; // 删文件
fs::remove_dir("data/logs")?; // 删空目录
fs::remove_dir_all("data")?; // 连目录带里面的一切,全删remove_dir_all 是"一键清空"——用前确认路径没错(错误处理篇:删除失败要报告)。
display() 与 to_string_lossy()
Path 打印用 path.display()(专门给路径的显示格式);path.to_string_lossy() 转成字符串(遇到非 UTF-8 文件名用 � 代替,不崩溃)。直接 to_string() 会 panic,别用。
常见误用
create_dir和create_dir_all混用:多级路径用all,用错会报"父目录不存在"read_to_string读二进制:报错"stream did not contain valid UTF-8"——二进制用readPath::new拼路径用+:Path不支持+,只能join- 循环里频繁
exists()/metadata():每次都是系统调用,循环里缓存结果(性能篇的坑)