Rust 文件 I/O 操作方法总结
📁 一、核心模块
use std::fs; // 文件系统操作
use std::io; // I/O 基础特性
use std::path::Path; // 路径处理
use std::fs::{File, OpenOptions}; // 文件句柄
📄 二、文件读取
1. 一次性读取整个文件
// 读取为字符串
let content = fs::read_to_string("file.txt")?;
// 读取为字节向量
let bytes = fs::read("file.bin")?;
2. 使用 File 句柄逐块读取
use std::fs::File;
use std::io::{Read, BufReader};
// 打开文件
let mut file = File::open("data.txt")?;
// 方法1: 读取到字符串
let mut content = String::new();
file.read_to_string(&mut content)?;
// 方法2: 读取到字节缓冲区
let mut buffer = [0u8; 1024]; // 1KB 缓冲区
let bytes_read = file.read(&mut buffer)?;
// 方法3: 使用 BufReader 提高性能
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
3. 逐行读取
use std::fs::File;
use std::io::{BufRead, BufReader};
let file = File::open("file.txt")?;
let reader = BufReader::new(file);
// 方法1: 使用迭代器
for (index, line) in reader.lines().enumerate() {
let line = line?;
println!("Line {}: {}", index + 1, line);
}
// 方法2: 收集所有行
let lines: Vec<String> = reader.lines().collect::<Result<_, _>>()?;
✍️ 三、文件写入
1. 一次性写入
// 写入字符串
fs::write("output.txt", "Hello, World!")?;
// 写入字节
fs::write("data.bin", vec![0x48, 0x65, 0x6C, 0x6C, 0x6F])?;
2. 使用 File 句柄写入
use std::fs::File;
use std::io::Write;
// 创建文件(会清空已存在的内容)
let mut file = File::create("output.txt")?;
// 写入字符串
file.write_all(b"Hello, World!\n")?;
writeln!(file, "Another line")?; // 使用 format! 宏
file.write_all(format!("Number: {}\n", 42).as_bytes())?;
// 追加写入
let mut file = OpenOptions::new()
.append(true)
.create(true) // 如果不存在则创建
.open("log.txt")?;
file.write_all(b"New log entry\n")?;
3. 使用 OpenOptions 精确控制
use std::fs::OpenOptions;
let file = OpenOptions::new()
.read(true) // 可读
.write(true) // 可写
.create(true) // 不存在则创建
.truncate(true) // 打开时清空
.open("data.txt")?;
🔄 四、文件复制、移动、删除
1. 复制文件
// 简单复制
fs::copy("source.txt", "dest.txt")?;
// 带进度的复制
use std::fs::{File, copy};
use std::io::{Read, Write};
fn copy_with_progress(src: &str, dst: &str) -> io::Result<()> {
let mut source = File::open(src)?;
let mut dest = File::create(dst)?;
let mut buffer = [0; 8192]; // 8KB 缓冲区
loop {
let bytes_read = source.read(&mut buffer)?;
if bytes_read == 0 { break; }
dest.write_all(&buffer[..bytes_read])?;
}
Ok(())
}
2. 移动/重命名文件
fs::rename("old_name.txt", "new_name.txt")?;
3. 删除文件
// 删除文件
fs::remove_file("file.txt")?;
// 强制删除(忽略错误)
let _ = fs::remove_file("file.txt");
📁 五、目录操作
1. 创建目录
// 创建单级目录
fs::create_dir("new_dir")?;
// 递归创建多级目录
fs::create_dir_all("path/to/nested/dir")?;
2. 读取目录内容
use std::fs;
// 读取目录项
for entry in fs::read_dir(".")? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
println!("File: {:?}", path.file_name());
} else if path.is_dir() {
println!("Dir: {:?}", path.file_name());
}
// 获取元数据
let metadata = entry.metadata()?;
println!("Size: {} bytes", metadata.len());
}
3. 遍历目录树
use walkdir::WalkDir; // 需要 walkdir crate
// 递归遍历所有文件
for entry in WalkDir::new(".")
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
{
if entry.file_type().is_file() {
println!("{}", entry.path().display());
}
}
4. 删除目录
// 删除空目录
fs::remove_dir("empty_dir")?;
// 递归删除目录(包括内容)
fs::remove_dir_all("dir_with_contents")?;
📊 六、文件元数据
use std::fs;
use std::time::SystemTime;
let metadata = fs::metadata("file.txt")?;
println!("文件大小: {} bytes", metadata.len());
println!("是否只读: {}", metadata.permissions().readonly());
println!("是否为文件: {}", metadata.is_file());
println!("是否为目录: {}", metadata.is_dir());
println!("是否为符号链接: {}", metadata.file_type().is_symlink());
// 时间信息
if let Ok(modified) = metadata.modified() {
println!("最后修改: {:?}", modified);
}
if let Ok(accessed) = metadata.accessed() {
println!("最后访问: {:?}", accessed);
}
if let Ok(created) = metadata.created() {
println!("创建时间: {:?}", created);
}
🔐 七、文件权限
use std::fs;
use std::os::unix::fs::PermissionsExt; // Unix 系统
let metadata = fs::metadata("file.txt")?;
let permissions = metadata.permissions();
// Unix 系统:检查权限
#[cfg(unix)]
{
println!("权限: {:o}", permissions.mode());
// 检查可读
if permissions.mode() & 0o400 != 0 {
println!("文件可读");
}
// 修改权限
let mut perms = permissions.clone();
perms.set_mode(0o755); // rwxr-xr-x
fs::set_permissions("file.txt", perms)?;
}
// 跨平台:设置只读
let mut perms = fs::metadata("file.txt")?.permissions();
perms.set_readonly(true);
fs::set_permissions("file.txt", perms)?;
🧪 八、文件锁
1. 独占锁(排他锁)
use std::fs::OpenOptions;
use std::io::{Write, Read};
use fs2::FileExt; // 需要 fs2 crate
let file = OpenOptions::new()
.write(true)
.create(true)
.open("data.txt")?;
// 尝试获取独占锁
if let Err(e) = file.try_lock_exclusive() {
eprintln!("文件已被锁定: {}", e);
} else {
// 执行写入操作
file.write_all(b"独占写入")?;
// 锁会在文件关闭时自动释放
}
2. 共享锁(读锁)
let file = OpenOptions::new()
.read(true)
.open("data.txt")?;
// 获取共享锁(允许多个读取)
file.lock_shared()?;
let mut content = String::new();
file.read_to_string(&mut content)?;
file.unlock()?; // 显式解锁
📁 九、临时文件
1. 标准库临时文件
use tempfile::NamedTempFile; // 需要 tempfile crate
// 创建临时文件(自动删除)
let mut tmp_file = NamedTempFile::new()?;
writeln!(tmp_file, "临时数据")?;
// 获取路径
let tmp_path = tmp_file.path();
println!("临时文件: {:?}", tmp_path);
// 文件在离开作用域时自动删除
2. 临时目录
use tempfile::tempdir;
let tmp_dir = tempdir()?;
let file_path = tmp_dir.path().join("my_temp.txt");
fs::write(&file_path, "临时数据")?;
// 目录在离开作用域时自动删除
🚀 十、异步文件 I/O
// 使用 tokio
use tokio::fs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn async_file_operations() -> io::Result<()> {
// 异步读取
let content = fs::read_to_string("file.txt").await?;
// 异步写入
fs::write("output.txt", "异步写入").await?;
// 异步文件句柄
let mut file = fs::File::open("data.txt").await?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await?;
Ok(())
}
📈 十一、性能优化技巧
1. 缓冲区大小优化
// 使用适当的缓冲区大小
const CHUNK_SIZE: usize = 8192; // 8KB 通常是最优的
let mut file = File::open("large.bin")?;
let mut buffer = vec![0u8; CHUNK_SIZE];
while let Ok(bytes_read) = file.read(&mut buffer) {
if bytes_read == 0 { break; }
// 处理数据
}
2. 零拷贝读取
use std::fs;
use std::io;
// mmap 内存映射(需要 memmap crate)
use memmap2::Mmap;
let file = fs::File::open("data.bin")?;
let mmap = unsafe { Mmap::map(&file)? };
// 直接访问内存,无需复制
let data = &mmap[..];
3. 批量操作
// 批量读取文件列表
use rayon::prelude::*; // 并行处理
let files = vec!["file1.txt", "file2.txt", "file3.txt"];
let contents: Vec<String> = files
.par_iter() // 并行迭代
.filter_map(|f| fs::read_to_string(f).ok())
.collect();
🔧 十二、错误处理模式
1. 自定义错误类型
use thiserror::Error; // 需要 thiserror crate
#[derive(Error, Debug)]
enum FileError {
#[error("IO错误: {0}")]
Io(#[from] io::Error),
#[error("文件不存在: {0}")]
NotFound(String),
#[error("权限不足: {0}")]
PermissionDenied(String),
}
fn read_file_safely(path: &str) -> Result<String, FileError> {
fs::read_to_string(path)
.map_err(|e| match e.kind() {
io::ErrorKind::NotFound => FileError::NotFound(path.to_string()),
io::ErrorKind::PermissionDenied => FileError::PermissionDenied(path.to_string()),
_ => FileError::Io(e),
})
}
2. 错误传播简化
use anyhow::{Context, Result}; // 需要 anyhow crate
fn process_file(path: &str) -> Result<()> {
let content = fs::read_to_string(path)
.with_context(|| format!("读取文件失败: {}", path))?;
// 处理内容...
Ok(())
}
📋 十三、常用工具函数
/// 检查文件是否存在
fn file_exists(path: &str) -> bool {
Path::new(path).exists()
}
/// 获取文件大小
fn get_file_size(path: &str) -> io::Result<u64> {
fs::metadata(path).map(|m| m.len())
}
/// 获取文件扩展名
fn get_extension(path: &str) -> Option<&str> {
Path::new(path)
.extension()
.and_then(|ext| ext.to_str())
}
/// 安全删除文件(备份)
fn safe_remove(path: &str) -> io::Result<()> {
let backup_path = format!("{}.bak", path);
fs::rename(path, &backup_path)?;
fs::remove_file(backup_path)
}
🎯 十四、最佳实践
- 始终处理 I/O 错误:使用
Result和?操作符 - 使用
Path替代字符串:更好的跨平台兼容性 - 及时关闭文件:避免文件描述符泄漏
- 大文件使用流式处理:避免内存溢出
- 注意文件锁:多进程/多线程环境
- 清理临时文件:使用 RAII 模式
- 日志记录操作:便于调试和审计
- 权限检查:特别是跨平台应用
📦 十五、常用 Crate 推荐
| Crate | 用途 | 特点 |
|---|---|---|
walkdir |
递归目录遍历 | 简单易用,性能好 |
tempfile |
临时文件/目录 | 自动清理,安全 |
fs2 |
文件锁 | 跨平台文件锁 |
memmap2 |
内存映射 | 零拷贝读取 |
serde_json |
JSON 文件 | 结构化数据读写 |
csv |
CSV 文件 | 表格数据处理 |
anyhow |
错误处理 | 简化错误传播 |
thiserror |
错误定义 | 自定义错误类型 |
Rust 的文件 I/O 系统设计兼顾了安全性和性能,通过所有权系统和 Result 类型确保了内存安全和错误处理,同时提供了丰富的 API 来满足各种文件操作需求。