rust学习之文件操作概述
前言
rust的文件操作属于标准库,在std::fs包下面。下面的东西很多,我一下也没办法都研究清楚,所以写一个简单的,把基础的文件操作总结下。
文件的生命周期
对于文件系统来说,在windos和Linux下面的原生api是不同的。但是一般高级的编程语言会做自己的抽象,将两个平台的api统一。rust提供了统一的抽象api,也提供了每个平台底层的api。这里我们介绍的都是rust提供的抽象api。
一个文件在创建后,经历的生命周期一般如下图所示,我们后面的介绍也是根据这个图。
create
创建文件,目前我所知道的方法是std::fs::File::create(),源码如下。
[me]: 其实从源码看,还可以用OpenOptions来实现。创建操作其实包含在打开的概念里面的。
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
例子:
use std::fs;
use std::fs::File;
fn main() {
let result = File::create("./test.txt");
println!("{:?}", result.unwrap())
}
这里看源码的时候,我有一个疑惑,create函数的入参是AsRef<Path>的trait,但是example里面都是直接用的string入参,后来在string的源码里面可以发现string和str类型都是实现了AsRef<Path>这个trait。
所以目前可以通过三种方式入参:
- string
- &str
- path strcut
[me]:这里感觉就设计的很巧妙,如果在java里面实现类似的效果,我们首先想到的是都是方法重载吧,不过这样要多写很多代码。但是在rust里面非常灵活,只需要让三个类型去实现AsRef<Path>的trait。
open
File::open()函数,打开一个存在的文件,默认是只读模式。返回结果是一个Eesutl,如果文件不存在,Err是no such file or directory。源码如下:
#[stable(feature = "rust1", since = "1.0.0")]
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().read(true).open(path.as_ref())
}
例子:
use std::fs;
use std::fs::File;
fn main() {
let result = File::open("./one.txt");
println!("{:?}", result.unwrap())
}
看源码底层操作还是用的OpenOptions,所以如果我们加了一个参数,就可以改为不存在文件的时候创建。
ps:如果想创建文件必须设置write或append为true。
let result1 = OpenOptions::new().write(true).create(true).open("./one.txt");
println!("{:?}", result1.unwrap());
copy
复制文件的原理是byte级的复制。如果原文件不存在Result是文件不存在的err。如果目标文件不存在会直接创建。目标文件存在会被直接覆盖。成功的Result是成功拷贝的byte size。源码如下
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
fs_imp::copy(from.as_ref(), to.as_ref())
}
例子:
use std::fs;
use std::fs::{File, OpenOptions};
fn main() {
let result = fs::copy("hello.txt", "111.txt");
println!("{:?}", result.unwrap());
}
rename
重新命名一个文件。如果文件不存在或者用户权限不够会导致失败。如果重命名文件存在会直接替换。源码如下:
#[stable(feature = "rust1", since = "1.0.0")]
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
fs_imp::rename(from.as_ref(), to.as_ref())
}
例子:
use std::fs;
use std::fs::{File, OpenOptions};
fn main() {
let result = fs::rename("hello.txt", "hello_world.txt");
println!("{:?}", result.unwrap())
}
read
读取文件的字符。有两个方法一个是直接读取文件,然后返回vec的字符。一个是经典的传一个数组进去,然后填充数组的方式。
[me]:不展示源码了,源码太底层我也没看懂。
例子:
use std::fs;
use std::fs::{File, OpenOptions};
use std::io::Read;
fn main() {
let result1 = fs::read("./111.txt");
println!("{:?}", result1.unwrap());
let result= OpenOptions::new().read(true).write(true).open("./111.txt");
let mut temp: [u8; 100] = [0; 100];
let result2 = result.unwrap().read(&mut temp);
println!("{:?}", temp)
}
read_to_string
升级的方式,简单好用。读取文件并且返回一个string类型。还有一个方法,不太好用...
例子:
use std::fs;
use std::fs::{File, OpenOptions};
use std::io::Read;
fn main() {
let result = fs::read_to_string("./111.txt");
let result1 = OpenOptions::new().read(true).write(true).open("./111.txt");
let mut temp = String::new();
let result2 = result1.unwrap().read_to_string(&mut temp);
println!("{}", result.unwrap());
println!("{}", temp);
}
write
fs::write 打开一个文件,并且覆盖写内容。也就是说不能追加内容,另外如果文件不存在直接创建。这个函数的好处是参数是string,使用起来方便。另外可以通过file struct的write方法写,还可以追加写,但是参数是u8 array。使用起来不太方便。
例子:
use std::fs;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
fn main() {
let result = fs::write("./111.txt", "hello write");
}
query
这里的query是根据文件的信息(metadata)查找的意思,并没有专门的api。文件的metadata主要有,大小(len),是否是目录(is_dir),权限,是否可以更改等等。
[me]:也可以通过file struct 获取metadata。
use std::fs;
fn main() {
let file_metadata = fs::metadata("111.txt").unwrap();
println!("Len: {}, last accessed: {:?}, modified : {:?}, created: {:?}", file_metadata.len(), file_metadata.accessed(), file_metadata.modified(), file_metadata.created());
println!("Is file: {}, Is dir: {}, is Symlink: {}", file_metadata.is_file(),
file_metadata.is_dir(), file_metadata.file_type().is_symlink());
println!("Permissions of file are: {:?}", file_metadata.permissions());
}
close
得益于所有权机制,rust不用我们显示调用close方法。file struct在离开自己的作用域的时候,会自动把自己close掉^_^。
总结
总结下文件操作有三种思路:
- fs包下面的函数操作。最全面的,最方便。
- file struct里面的函数和方法。不全面,不好用。
- 通过OpenOptions获取file struct操作。最全面,不方便,但是最精细的控制。
这下可以用rust来操作文件了,但是还不够。因为文件系统还有dir和path两个大魔头要打败才能掌握。