Skip to content

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)
}

🎯 十四、最佳实践

  1. 始终处理 I/O 错误:使用 Result? 操作符
  2. 使用 Path 替代字符串:更好的跨平台兼容性
  3. 及时关闭文件:避免文件描述符泄漏
  4. 大文件使用流式处理:避免内存溢出
  5. 注意文件锁:多进程/多线程环境
  6. 清理临时文件:使用 RAII 模式
  7. 日志记录操作:便于调试和审计
  8. 权限检查:特别是跨平台应用

📦 十五、常用 Crate 推荐

Crate 用途 特点
walkdir 递归目录遍历 简单易用,性能好
tempfile 临时文件/目录 自动清理,安全
fs2 文件锁 跨平台文件锁
memmap2 内存映射 零拷贝读取
serde_json JSON 文件 结构化数据读写
csv CSV 文件 表格数据处理
anyhow 错误处理 简化错误传播
thiserror 错误定义 自定义错误类型

Rust 的文件 I/O 系统设计兼顾了安全性和性能,通过所有权系统和 Result 类型确保了内存安全和错误处理,同时提供了丰富的 API 来满足各种文件操作需求。