Skip to content

Rust - 文件输入/输出

现代应用程序经常需要与文件系统交互。Rust 提供了一个健壮且安全的标准库模块 std::fs,用于处理文件输入/输出(I/O)。本教程涵盖了在 Rust 中使用文件的基本现代实践。

几乎每个文件操作都可能因程序无法控制的原因(例如,文件未找到、权限被拒绝)而失败。Rust 使用 Result<T, E> 枚举来表示这些结果。与使用 .unwrap() 或 .expect() 导致崩溃不同,现代 Rust 代码使用 ? 运算符传播错误。这使得代码更简洁、更健壮。

要使用 ? 运算符,封闭函数必须返回 Result 类型。对于 main 函数,一个常见的签名是 fn main() -> std::io::Result<()>,这表示它可以返回任何标准 I/O 错误。

std::fs 模块既提供高级便捷函数,也通过 File 结构体提供更底层的控制。

No.函数 / 结构体签名 (简化)描述
1std::fs::writefn write<P, C>(path: P, contents: C) -> io::Result<()>这是一个便捷函数,用于打开文件、写入所有内容并关闭。如果文件不存在则创建,如果文件存在则覆盖。
2std::fs::read_to_stringfn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String>这是一个便捷函数,用于打开文件、将其全部内容读取到 String 中并关闭。
3std::fs::File::createfn create<P: AsRef<Path>>(path: P) -> io::Result<File>以只写模式打开文件。创建新文件或截断现有文件。返回一个 File 句柄,用于更复杂的操作。
4std::fs::File::openfn open<P: AsRef<Path>>(path: P) -> io::Result<File>以只读模式打开现有文件。返回一个 File 句柄。
5std::fs::OpenOptionsstruct OpenOptions提供对文件打开方式(读、写、追加、创建等)的详细控制。这是最灵活的方法。
6std::fs::remove_filefn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()>从文件系统中删除文件。
7std::io::Write::write_allfn write_all(&mut self, buf: &[u8]) -> io::Result<()>File 上的一个特性方法,尝试写入整个字节缓冲区。
8std::io::Read::read_to_stringfn read_to_string(&mut self, buf: &mut String) -> io::Result<usize>File 上的一个特性方法,读取所有字节直到文件结束符(EOF)到字符串缓冲区中。

对于简单任务,使用高级函数 fs::write 和 fs::read_to_string 是最直接的方式。请注意 main 函数的返回类型。

use std::fs;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let file_path = "greeting.txt";
let content = "Hello, Modern Rust!\nThis is a new line.";
// 将内容写入文件,如果存在则覆盖
fs::write(file_path, content)?;
println!("Successfully wrote to {}", file_path);
// 将整个文件读取到字符串中
let read_content = fs::read_to_string(file_path)?;
println!("\n--- Reading from file ---");
print!("{}", read_content);
Ok(())
}
Successfully wrote to greeting.txt
--- Reading from file ---
Hello, Modern Rust!
This is a new line.

若要追加内容而不覆盖,我们使用 OpenOptions。我们通过链式调用方法来配置所需行为,然后调用 .open()。

use std::fs::OpenOptions;
use std::io::Write;
fn main() -> std::io::Result<()> {
let file_path = "log.txt";
let mut file = OpenOptions::new()
.append(true) // 设置追加模式
.create(true) // 如果文件不存在则创建
.open(file_path)?;
file.write_all(b"LOG: Application started.\n")?;
println!("Successfully appended to log file.");
Ok(())
}

std::fs 模块还包含用于常见文件系统管理任务的简单函数。

use std::fs;
fn main() -> std::io::Result<()> {
// 首先,确保我们有一个可以操作的文件
fs::write("source.txt", "This is the original file.")?;
// 复制文件
fs::copy("source.txt", "destination.txt")?;
println!("Copied source.txt to destination.txt");
// 读取新文件进行验证
let content = fs::read_to_string("destination.txt")?;
println!("Content of destination.txt: '{}'", content);
// 删除原始文件
fs::remove_file("source.txt")?;
println!("Removed source.txt");
Ok(())
}

当读取或写入大文件时,进行许多小的系统调用可能效率低下。Rust 的 std::io 模块提供了 BufReader 和 BufWriter,用于在内存中缓冲操作,从而减少系统调用并提高性能。

use std::fs::File;
use std::io::{Write, BufWriter};
fn main() -> std::io::Result<()> {
let file = File::create("large_log.txt")?;
let mut writer = BufWriter::new(file);
for i in 0..1000 {
writeln!(writer, "Entry number {}", i)?;
}
// 当 `writer` 超出作用域时,缓冲区会自动刷新。
println!("Wrote 1000 entries with a BufWriter.");
Ok(())
}