#std::fs 模块

Rust 的 std::fs 模块提供了文件系统操作的工具,包括文件和目录的创建、读取、写入、删除和元数据访问等。它是标准库的一部分,用于处理本地文件系统任务。std::fs 的函数多返回 std::io::Result 以处理错误,如文件不存在或权限不足,确保安全性和显式错误处理。模块常与 std::pathstd::io 结合使用。

1. std::fs 简介

  • 导入use std::fs;
  • 主要类型和函数
    • File:文件句柄,用于读写(从 std::fs::File)。
    • DirEntry:目录条目,用于读取目录。
    • 函数read/write(字节)、read_to_string/write(字符串)、create_dir/remove_dir(目录)、metadata(元数据)、copy/rename/remove_file(文件操作)。
  • 优势:跨平台(Windows/Unix 处理差异)、线程安全、集成 I/O trait。
  • 注意:操作可能失败(返回 Result),如权限或路径无效。路径用 std::path::Path 以确保兼容。

2. 文件读写

std::fs 提供简单函数读取/写入整个文件。

示例:读取文件为字符串

use std::fs;

fn main() -> std::io::Result<()> {
    let contents = fs::read_to_string("example.txt")?;
    println!("文件内容: {}", contents);
    Ok(())
}
  • 解释read_to_string 读取整个文件为 String。如果文件不存在,返回 Err(io::Error { kind: NotFound })。用 ? 传播错误。类似:read 返回 Vec(字节)。

示例:写入文件

use std::fs;

fn main() -> std::io::Result<()> {
    fs::write("output.txt", "Hello, Rust!")?;
    println!("文件已写入");
    Ok(())
}
  • 解释write 覆盖或创建文件,接受 &str 或 &[u8]。如果目录不存在,返回 Err。其他:OpenOptions 用于自定义打开模式(如追加)。

示例:使用 File 句柄读写

use std::fs::File;
use std::io::{Read, Write};

fn main() -> std::io::Result<()> {
    let mut file = File::create("file.txt")?;
    file.write_all(b"一些字节")?;

    let mut contents = String::new();
    let mut file = File::open("file.txt")?;
    file.read_to_string(&mut contents)?;
    println!("读取: {}", contents);

    Ok(())
}
  • 解释File::create 创建/覆盖文件。File::open 只读打开。集成 ReadWrite trait。用于大文件时,考虑 BufReader/BufWriter 以缓冲。

3. 目录操作

管理目录创建、读取和删除。

示例:创建和删除目录

use std::fs;

fn main() -> std::io::Result<()> {
    fs::create_dir("new_dir")?;  // 创建单个目录
    fs::create_dir_all("nested/dir/path")?;  // 创建嵌套目录

    fs::remove_dir("new_dir")?;  // 删除空目录
    fs::remove_dir_all("nested")?;  // 递归删除

    Ok(())
}
  • 解释create_dir_all 创建中间目录。如果目录已存在,返回 Err(AlreadyExists)。remove_dir_all 危险,用于非空目录;小心使用。

示例:读取目录

use std::fs;

fn main() -> std::io::Result<()> {
    let paths = fs::read_dir(".")?;  // 当前目录
    for path in paths {
        let entry = path?;
        println!("路径: {}, 类型: {:?}", entry.path().display(), entry.file_type()?);
    }
    Ok(())
}
  • 解释read_dir 返回 DirEntry 迭代器。DirEntrypath()metadata()file_type()(FileType 如 is_dir())。处理迭代中的 Result。

4. 文件元数据和操作

访问文件信息和执行复制/重命名。

示例:文件元数据

use std::fs;

fn main() -> std::io::Result<()> {
    let metadata = fs::metadata("example.txt")?;
    println!("大小: {} 字节", metadata.len());
    println!("是文件?{}", metadata.is_file());
    println!("修改时间: {:?}", metadata.modified()?);

    let permissions = metadata.permissions();
    println!("只读?{}", permissions.readonly());

    Ok(())
}
  • 解释metadata 返回 Metadata。len() 文件大小。permissions() 返回 Permissions(readonly/set_readonly)。modified() 返回 SystemTime。

示例:复制、重命名和删除文件

use std::fs;

fn main() -> std::io::Result<()> {
    fs::copy("source.txt", "dest.txt")?;  // 复制
    fs::rename("dest.txt", "new_name.txt")?;  // 重命名/移动
    fs::remove_file("new_name.txt")?;  // 删除

    Ok(())
}
  • 解释copy 复制内容。rename 可跨目录(同分区)。remove_file 只删文件(非目录)。

5. 符号链接和硬链接

创建链接(Unix-like 系统支持更好)。

示例:创建符号链接

use std::fs;
use std::os::unix::fs::symlink;  // Unix 特定

#[cfg(unix)]
fn main() -> std::io::Result<()> {
    symlink("original.txt", "link.txt")?;
    let target = fs::read_link("link.txt")?;
    println!("链接指向: {}", target.display());

    Ok(())
}
  • 解释symlink 创建软链接(Windows 用 std::os::windows::fs::symlink_file)。read_link 返回目标路径。hard_link 创建硬链接。用 #[cfg] 条件编译平台特定代码。

6. 高级主题:OpenOptions 和 Canonicalize

  • OpenOptions:自定义文件打开(如追加、只读)。
  • canonicalize:解析绝对路径,展开链接。

示例:OpenOptions

use std::fs::OpenOptions;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut file = OpenOptions::new()
        .write(true)
        .append(true)
        .create(true)
        .open("append.txt")?;
    file.write_all(b" 追加内容")?;

    Ok(())
}
  • 解释OpenOptions 链式配置。append 追加模式。create 如果不存在创建。

示例:Canonicalize

use std::fs;

fn main() -> std::io::Result<()> {
    let abs_path = fs::canonicalize("relative/path.txt")?;
    println!("绝对路径: {}", abs_path.display());
    Ok(())
}
  • 解释canonicalize 返回绝对 PathBuf,解析 ../ 和符号链接。

7. 最佳实践和常见陷阱

  • 错误处理:总是用 ? 或 match 处理 Result(如 NotFound、PermissionDenied)。
  • 路径:用 Path::new 创建路径,避免硬编码分隔符(/ vs \)。
  • 安全性:避免用户输入路径(路径注入);用 canonicalize 规范化。
  • 性能:大文件用 BufReader/Writer;目录迭代用 read_dir 而非递归。
  • 跨平台:测试 Windows/Unix;符号链接在 Windows 需管理员权限。
  • 常见错误
    • 文件不存在:Err(NotFound) – 检查前用 Path::exists()。
    • 权限:Err(PermissionDenied) – 运行时检查。
    • 非空目录删除:remove_dir 失败,用 remove_dir_all。
  • 与 walkdir crate:复杂遍历用外部 crate。
  • 线程:fs 操作线程安全,但并发写入需锁。

练习建议

  1. 编写工具:复制目录树,用 read_dir 递归。
  2. 创建日志函数:用 OpenOptions 追加写入文件。
  3. 检查文件修改:用 metadata 比较时间。