Rust 中的文件操作
文件路径
想要打开或者创建一个文件,首先要指定文件的路径。
Rust 中的路径操作是跨平台的,std::path
模块提供的了两个用于描述路径的类型:
PathBuf
-- 具有所有权并且可被修改,类似于String
。Path
-- 路径切片,类似于str
。
示例:
1use std::path::Path;
2use std::path::PathBuf;
3fn main() {
4 // 直接将一个字符串切片包装成一个路径切片
5 let path = Path::new("./foo/bar.txt");
6
7 // 返回上级路径,若无上级路径则返回 `None`
8 let parent = path.parent().unwrap();
9
10 // 返回文件名(不包含文件扩展名)
11 let file_stem = path.file_stem().unwrap();
12
13 println!(
14 "path: {:?}, parent: {:?}, file_stem: {:?}",
15 path, parent, file_stem
16 );
17
18 // 创建一个空的 `PathBuf`
19 let mut empty_path = PathBuf::new();
20 println!("empty_path: {:?}", empty_path);
21
22 // 根据字符串切片创建 `PathBuf`
23 let path = PathBuf::from(r"C:\windows\system32.dll");
24
25 // 添加路径
26 empty_path.push(r"C:\");
27
28 println!("empty_path: {:?}, path: {:?}", empty_path, path);
29}
文件创建和删除
Rust 的 std::fs
模块提供了一系列文件系统操作的功能。
目录创建和删除
创建目录的函数:
create_dir<P: AsRef<Path>>(path: P) -> Result<()>
-- 创建一个空目录,若指定路径不存在则会返回错误。create_dir_all<P: AsRef<Path>>(path: P) -> Result<()>
-- 级联创建目录。
示例:
1use std::fs;
2// 由于字符串切片实现了 `AsRef<Path>` Trait,因此函数中的参数可以直接使用字符串字面量
3fn main() -> std::io::Result<()> {
4 // 创建一个空目录
5 fs::create_dir("./empty")?;
6
7 // 创建一个目录,若其上级目录不存在,则一同创建
8 fs::create_dir_all("./some/dir")?;
9
10 Ok(())
11}
删除目录的函数:
remove_dir<P: AsRef<Path>>(path: P) -> Result<()>
-- 删除指定空目录。remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()>
-- 删除指定目录及其目录下的内容。
示例:
1use std::fs;
2
3fn main() -> std::io::Result<()> {
4 // 删除一个空目录
5 fs::remove_dir("./empty")?;
6
7 // 删除指定目录及其目录下的内容,但不会删除其上级目录
8 fs::remove_dir_all("./some/dir")?;
9
10 Ok(())
11}
文件创建和删除
Rust 使用 std::fs::File
结构体与文件系统中的文件相关联,通过 std::fs::File
实例可以对文件进行读取和写入。
文件创建和删除的函数:
create<P: AsRef<Path>>(path: P) -> Result<File>
std::fs::remove_file<P: AsRef<Path>>(path: P) -> Result<()>
示例:
1use std::fs;
2use std::fs::File;
3
4fn main() -> std::io::Result<()> {
5 // 以只写模式打开指定文件,若文件存在则清空文件内容,若文件不存在则新建一个
6 let mut f = File::create("foo.txt")?;
7
8 // 删除文件
9 fs::remove_file("foo.txt")?;
10
11 Ok(())
12}
文件读取和写入
std::fs::File
本身实现了 Read
和 Write
Trait,所以文件的读写非常简单。
文件打开
在读写文件之前,首先应该得到一个 File
类型实例。除了可以在创建文件时获取 File
实例,还可以使用 File
的 open
函数:
open<P: AsRef<Path>>(path: P) -> Result<File>
示例:
1use std::fs::File;
2
3fn main() -> std::io::Result<()> {
4 // 以只读模式打开指定文件,若文件不存在则返回错误
5 let _file = File::open("foo.txt")?;
6
7 Ok(())
8}
使用 create
和 open
函数获取的 File 实例是只读或者只写的,如果想要控制更多的读写选项,则需要使用 std::fs::OpenOptions
。它是一个 Builder,create
和 open
函数的底层也是这个 Builder。
使用 std::fs::OpenOptions
时,首先调用 OpenOptions::new
,然后通过链式调用来设置读写选项,最后调用 OpenOptions::open
打开指定的文件。
示例:
1use std::fs::OpenOptions;
2
3fn main() -> std::io::Result<()> {
4 let _file = OpenOptions::new()
5 .read(true)
6 .write(true)
7 .create(true) // 新建,若文件存在则打开这个文件
8 .open("foo.txt")?;
9
10 let _file = OpenOptions::new()
11 .append(true) // 追加内容
12 .open("foo.txt")?;
13
14 let _file = OpenOptions::new()
15 .write(true)
16 .truncate(true) // 清空文件
17 .open("foo.txt");
18
19 Ok(())
20}
文件读取
读取文件主要用的是 std::io::Read
Trait 中的函数。比如:
read(&mut self, buf: &mut [u8]) -> Result<usize>
read_to_string(&mut self, buf: &mut String) -> Result<usize>
示例:
1use std::fs::File;
2use std::io;
3// `prelude` 模块包含通常使用的 IO Trait: `BufRead`, `Read`, `Write`, `Seek`
4use std::io::prelude::*;
5
6fn main() -> io::Result<()> {
7 let mut f = File::open("foo.txt")?;
8 let mut buffer = [0; 10];
9 // 读取文件中的前10个字节
10 let n = f.read(&mut buffer[..])?;
11 println!("The bytes: {:?}", &buffer[..n]);
12
13 // 接着读取10个字节
14 let n = f.read(&mut buffer[..])?;
15 println!("The bytes: {:?}", &buffer[..n]);
16
17 let mut f = File::open("foo.txt")?;
18 let mut buffer = String::new();
19 // 读取文件所有内容并转为字符字符串,若文件非 UTF-8 格式,则会报错
20 f.read_to_string(&mut buffer)?;
21 println!("The string: {}", buffer);
22
23 Ok(())
24}
另外,File
类型还实现了 std::io::Seek
Trait,Seek
主要提供了一个 seek
函数,可以控制文件读取和写入的起始位置。
seek(&mut self, pos: SeekFrom) -> Result<u64>
示例:
1use std::fs::File;
2use std::io;
3use std::io::prelude::*;
4use std::io::SeekFrom;
5
6fn main() -> io::Result<()> {
7 let mut f = File::open("foo.txt")?;
8
9 // 将游标前移 10 个字节(游标的默认位置是 0)
10 f.seek(SeekFrom::Start(10))?;
11
12 // 将前 10 个字节之后的内容读取到 Buf 中
13 let mut buffer = String::new();
14 f.read_to_string(&mut buffer)?;
15 println!("The string: {}", buffer);
16
17 Ok(())
18}
除了可以设置文件读取的起点,还可以限制文件读取的长度。std::io::Read
提供了 take
函数来限制文件读取的长度。
take(self, limit: u64) -> Take<Self>
示例:
1use std::fs::File;
2use std::io;
3use std::io::prelude::\*;
4
5fn main() -> io::Result<()> {
6let f = File::open("foo.txt")?;
7let mut buffer = [0; 10];
8
9 // 限制读取长度最多为 5 字节
10 let mut handle = f.take(5);
11
12 handle.read(&mut buffer)?;
13 println!("buffer: {:?}", buffer);
14
15 Ok(())
16
17}
文件写入
读取文件主要用的是 std::io::Write
Trait 中的函数。比如:
fn write(&mut self, buf: &[u8]) -> Result<usize>
-- 尝试将 Buf 中的全部内容写入文件,有可能不成功。fn flush(&mut self) -> Result<()>
fn write_all(&mut self, buf: &[u8]) -> Result<()>
-- 持续调用write
,将 Buf 中的所有内容都写入文件。
示例:
1use std::fs::File;
2use std::io::prelude::*;
3
4fn main() -> std::io::Result<()> {
5 let mut buffer = File::create("foo.txt")?;
6
7 buffer.write(b"some bytes")?;
8
9 buffer.write_all(b"more bytes")?;
10
11 buffer.flush()?;
12
13 Ok(())
14}