Rust 进阶学习笔记(三):文件操作
IO,文件的增删,读写,流操作
本文属于我的 Rust 学习笔记 系列,您现在看到的这段话是本系列的固定起始语。
Rust 入门学习笔记以实际例子为主,讲解部分不是从零开始的,所以不建议纯萌新观看,读者最好拥有任意一种面向对象语言的基础,然后自己多多少少看过 Rust 的基本语法,刷过一点 rustlings。 来源:原子之音。当然也包含个人的一些补充。 视频 代码
Rust 进阶学习笔记以及实战的来源则五花八门,将会标注在下一行⬇️。
本节出处:原子之音 视频
基本操作
Rust 主要通过std::fs::File来实现文件操作。
结构体File可用于描述或操作一个文件。
File的所有方法都会返回一个Result枚举。
文件的打开、创建、删除
File::open使用只读模式打开一个文件。
- 返回文件句柄
- 文件不存在则抛出错误
File::create使用只写模式打开一个文件。 - 文件存在则清空
- 文件不存在则新建
- 返回文件句柄
std::fs::OpenOptions可设置为追加模式std::fs::remove_file从文件系统删除一个文件,注意删除文件的方法在 fs 下面,和 File 是没有关系的!
例子
use std::io::Write;
fn main() -> std::io::Result<()> {
// 打开
let f = std::fs::File::open("README.md");
println!("{:?}", f); // Ok(File { fd: 3, path: "/media/jmy/code/rust-by-practice/README.md", read: true, write: false })
// 若不存在,会返回 Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })
// 创建,注意是覆盖式的,会清空已存在文件
let mut f = std::fs::File::create("foo.txt")?;
println!("{:?}", f);
// 必须要 use std::io::Write; 将 Write 特质引入当前作用域
// ? 用于消除警告
f.write("中文".as_bytes())?; // 返回值是写入的 usize,可能部分写入
f.write_all(b"hello!")?; // 返回值是写入结果
std::fs::remove_file("foo.txt")?;
Ok(())
}读写操作
读取文件的方式
- 读取成一个数组
- 读取成一个完整字符串
- 逐行读取
use std::io::{BufRead, BufReader, Read, Write};
fn main() -> std::io::Result<()> {
let mut file = std::fs::File::open("README.md")?;
let mut data = Vec::new();
// u8 数组
file.read_to_end(&mut data)?;
println!("{:?}", data);
// 字符串
let mut file = std::fs::File::open("README.md")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
println!("{:?}", content);
// 一行
let mut file = std::fs::File::open("README.md")?;
// 流式
let reader = BufReader::new(file);
// 注意 line 是一个 result
// lines() 需要 BufRead 特质
for (index, line) in reader.lines().enumerate() {
let line = line?;
println!("{}. {}", index + 1, line);
}
Ok(())
}写文件的方式
- 新建
- 写字符串
- 写比特
- 追加:
std::fs::OpenOptions::new().append(true).open("foo.txt");
use std::env;
use std::io::Write;
fn main() -> std::io::Result<()> {
let temp_dir = env::temp_dir();
println!("{:?}", temp_dir);
let temp_file = temp_dir.join("temp.txt");
let mut file = std::fs::File::create(&temp_file)?; // 不加 ? 返回值会是一个 Result
// 写一行
writeln!(&mut file, "字符串")?;
// 写 byte
file.write(b"111111111111111111111111111111111111111111111111111111111111111111111111111111")?;
// 追加
let mut file = std::fs::OpenOptions::new().append(true).open(temp_file)?;
// 注意此处只是为了演示,实际上不能这么写,字符串会被截断
file.write_all("\n1111111111111111111111111".as_bytes())?;
Ok(())
}
// 文件内容
// cat /tmp/temp.txt
// 字符串
// 111111111111111111111111111111111111111111111111111111111111111111111111111111
// 1111111111111111111111111% 路径操作
Rust 路径操作都在std::fs下,包括create_dir、remove_dir等。
下面例子出处。
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
#[cfg(target_family = "unix")]
use std::os::unix;
#[cfg(target_family = "windows")]
use std::os::windows;
use std::path::Path;
// `% cat path` 命令的简单实现
fn cat(path: &Path) -> io::Result<String> {
let mut f = File::open(path)?;
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
// `% echo s > path` 命令的简单实现
fn echo(s: &str, path: &Path) -> io::Result<()> {
let mut f = File::create(path)?;
f.write_all(s.as_bytes())
}
// `% touch path` 命令的简单实现(忽略已存在的文件)
fn touch(path: &Path) -> io::Result<()> {
match OpenOptions::new().create(true).write(true).open(path) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
fn main() {
println!("`mkdir a`");
// 创建目录,返回 `io::Result<()>`
match fs::create_dir("a") {
Err(why) => println!("! {:?}", why.kind()),
Ok(_) => {},
}
println!("`echo hello > a/b.txt`");
// 可以使用 `unwrap_or_else` 方法简化之前的匹配
echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`mkdir -p a/c/d`");
// 递归创建目录,返回 `io::Result<()>`
fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`touch a/c/e.txt`");
touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`ln -s ../b.txt a/c/b.txt`");
// 创建符号链接,返回 `io::Result<()>`
#[cfg(target_family = "unix")] {
unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
}
#[cfg(target_family = "windows")] {
windows::fs::symlink_file("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
println!("! {:?}", why.to_string());
});
}
println!("`cat a/c/b.txt`");
match cat(&Path::new("a/c/b.txt")) {
Err(why) => println!("! {:?}", why.kind()),
Ok(s) => println!("> {}", s),
}
println!("`ls a`");
// 读取目录内容,返回 `io::Result<Vec<Path>>`
match fs::read_dir("a") {
Err(why) => println!("! {:?}", why.kind()),
Ok(paths) => for path in paths {
println!("> {:?}", path.unwrap().path());
},
}
println!("`rm a/c/e.txt`");
// 删除文件,返回 `io::Result<()>`
fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
println!("`rmdir a/c/d`");
// 删除空目录,返回 `io::Result<()>`
fs::remove_dir("a/c/d").unwrap_or_else(|why| {
println!("! {:?}", why.kind());
});
}
📝 系列导航
- 上一篇: Rust 进阶学习笔记(二):复合数据类型
- 下一篇: Rust 进阶学习笔记(四):宏
- 合集列表