• 【Rust】文件系统


           

    目录

    一、读取文件的字符串行

    二、避免读取写入同一文件

    三、使用内存映射随机访问文件

    四、过去 24 小时内修改过的文件名

    五、查找给定路径的循环

    六、递归查找重名文件

    七、使用给定断言递归查找所有文件

    八、跳过隐藏文件遍历目录

    九、在给定深度的目录,递归计算文件大小

    十、递归查找所有 png 文件

    十一、忽略文件名大小写,使用给定模式查找所有文件


            本文将介绍Rust的文件系统,涵盖文件读写、目录遍历,并给出代码示例。

    一、读取文件的字符串行

            我们向文件写入三行信息,然后使用 BufRead::lines 创建的迭代器 Lines 读取文件,一次读回一行。File 模块实现了提供 BufReader 结构体的 Read trait。File::create 打开文件 File 进行写入,File::open 则进行读取,代码如下:

    1. use std::fs::File;
    2. use std::io::{Write, BufReader, BufRead, Error};
    3. fn main() -> Result<(), Error> {
    4. let path = "lines.txt";
    5. let mut output = File::create(path)?;
    6. write!(output, "out")?;
    7. let input = File::open(path)?;
    8. let buffered = BufReader::new(input);
    9. for line in buffered.lines() {
    10. println!("{}", line?);
    11. }
    12. Ok(())
    13. }

    二、避免读取写入同一文件

            对文件使用 same_file::Handle 结构体,可以测试文件句柄是否等同。在本实例中,将对要读取和写入的文件句柄进行相等性测试。

    1. use same_file::Handle;
    2. use std::fs::File;
    3. use std::io::{BufRead, BufReader, Error, ErrorKind};
    4. use std::path::Path;
    5. fn main() -> Result<(), Error> {
    6. let path_to_read = Path::new("new.txt");
    7. let stdout_handle = Handle::stdout()?;
    8. let handle = Handle::from_path(path_to_read)?;
    9. if stdout_handle == handle {
    10. return Err(Error::new(
    11. ErrorKind::Other,
    12. "You are reading and writing to the same file",
    13. ));
    14. } else {
    15. let file = File::open(&path_to_read)?;
    16. let file = BufReader::new(file);
    17. for (num, line) in file.lines().enumerate() {
    18. println!("{} : {}", num, line?.to_uppercase());
    19. }
    20. }
    21. Ok(())
    22. }

    三、使用内存映射随机访问文件

           使用 memmap 创建文件的内存映射,并模拟文件的一些非序列读取。使用内存映射意味着您仅需索引一个切片,而不是使用 seek 方法来导航整个文件。

            Mmap::map 函数假定内存映射后的文件没有被另一个进程同时更改,否则会出现竞态条件。

    1. use memmap::Mmap;
    2. use std::fs::File;
    3. use std::io::{Write, Error};
    4. fn main() -> Result<(), Error> {
    5. write!(File::create("content.txt")?, "My hovercraft is full of eels!")?;
    6. let file = File::open("content.txt")?;
    7. let map = unsafe { Mmap::map(&file)? };
    8. let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
    9. assert_eq!(&map[3..13], b"hovercraft");
    10. let random_bytes: Vec<u8> = random_indexes.iter()
    11. .map(|&idx| map[idx])
    12. .collect();
    13. assert_eq!(&random_bytes[..], b"My loaf!");
    14. Ok(())
    15. }

    四、过去 24 小时内修改过的文件名

            通过调用 env::current_dir 获取当前工作目录,然后通过 fs::read_dir 读取目录中的每个条目,通过 DirEntry::path 提取条目路径,以及通过通过 fs::Metadata 获取条目元数据。Metadata::modified 返回条目自上次更改以来的运行时间 SystemTime::elapsed。Duration::as_secs 将时间转换为秒,并与 24 小时(24 * 60 * 60 秒)进行比较。Metadata::is_file 用于筛选出目录。

    1. use error_chain::error_chain;
    2. use std::{env, fs};
    3. error_chain! {
    4. foreign_links {
    5. Io(std::io::Error);
    6. SystemTimeError(std::time::SystemTimeError);
    7. }
    8. }
    9. fn main() -> Result<()> {
    10. let current_dir = env::current_dir()?;
    11. println!(
    12. "Entries modified in the last 24 hours in {:?}:",
    13. current_dir
    14. );
    15. for entry in fs::read_dir(current_dir)? {
    16. let entry = entry?;
    17. let path = entry.path();
    18. let metadata = fs::metadata(&path)?;
    19. let last_modified = metadata.modified()?.elapsed()?.as_secs();
    20. if last_modified < 24 * 3600 && metadata.is_file() {
    21. println!(
    22. "Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
    23. last_modified,
    24. metadata.permissions().readonly(),
    25. metadata.len(),
    26. path.file_name().ok_or("No filename")?
    27. );
    28. }
    29. }
    30. Ok(())
    31. }

    五、查找给定路径的循环

            使用 same_file::is_same_file 检测给定路径的循环。例如,可以通过软连接(符号链接)在 Unix 系统上创建循环:

    1. mkdir -p /tmp/foo/bar/baz
    2. ln -s /tmp/foo/ /tmp/foo/bar/baz/qux

            下面的实例将断言存在一个循环。

    1. use std::io;
    2. use std::path::{Path, PathBuf};
    3. use same_file::is_same_file;
    4. fn contains_loopAsRef>(path: P) -> io::Result<Option<(PathBuf, PathBuf)>> {
    5. let path = path.as_ref();
    6. let mut path_buf = path.to_path_buf();
    7. while path_buf.pop() {
    8. if is_same_file(&path_buf, path)? {
    9. return Ok(Some((path_buf, path.to_path_buf())));
    10. } else if let Some(looped_paths) = contains_loop(&path_buf)? {
    11. return Ok(Some(looped_paths));
    12. }
    13. }
    14. return Ok(None);
    15. }
    16. fn main() {
    17. assert_eq!(
    18. contains_loop("/tmp/foo/bar/baz/qux/bar/baz").unwrap(),
    19. Some((
    20. PathBuf::from("/tmp/foo"),
    21. PathBuf::from("/tmp/foo/bar/baz/qux")
    22. ))
    23. );
    24. }

    六、递归查找重名文件

            在当前目录中递归查找重复的文件名,只打印一次。

    1. use std::collections::HashMap;
    2. use walkdir::WalkDir;
    3. fn main() {
    4. let mut filenames = HashMap::new();
    5. for entry in WalkDir::new(".")
    6. .into_iter()
    7. .filter_map(Result::ok)
    8. .filter(|e| !e.file_type().is_dir()) {
    9. let f_name = String::from(entry.file_name().to_string_lossy());
    10. let counter = filenames.entry(f_name.clone()).or_insert(0);
    11. *counter += 1;
    12. if *counter == 2 {
    13. println!("{}", f_name);
    14. }
    15. }
    16. }

    七、使用给定断言递归查找所有文件

            在当前目录中查找最近一天内修改的 JSON 文件。使用 follow_links 确保软链接(符号链接)像普通目录和文件一样被按照当前查找规则执行。

    1. use error_chain::error_chain;
    2. use walkdir::WalkDir;
    3. error_chain! {
    4. foreign_links {
    5. WalkDir(walkdir::Error);
    6. Io(std::io::Error);
    7. SystemTime(std::time::SystemTimeError);
    8. }
    9. }
    10. fn main() -> Result<()> {
    11. for entry in WalkDir::new(".")
    12. .follow_links(true)
    13. .into_iter()
    14. .filter_map(|e| e.ok()) {
    15. let f_name = entry.file_name().to_string_lossy();
    16. let sec = entry.metadata()?.modified()?;
    17. if f_name.ends_with(".json") && sec.elapsed()?.as_secs() < 86400 {
    18. println!("{}", f_name);
    19. }
    20. }
    21. Ok(())
    22. }

    八、跳过隐藏文件遍历目录

            递归下行到子目录的过程中,使用 filter_entry 对目录中的条目传递 is_not_hidden 断言,从而跳过隐藏的文件和目录。Iterator::filter 可应用到要检索的任何目录 WalkDir::DirEntry,即使父目录是隐藏目录。

            根目录 "." 的检索结果,通过在断言 is_not_hidden 中使用 WalkDir::depth 参数生成。

    1. use walkdir::{DirEntry, WalkDir};
    2. fn is_not_hidden(entry: &DirEntry) -> bool {
    3. entry
    4. .file_name()
    5. .to_str()
    6. .map(|s| entry.depth() == 0 || !s.starts_with("."))
    7. .unwrap_or(false)
    8. }
    9. fn main() {
    10. WalkDir::new(".")
    11. .into_iter()
    12. .filter_entry(|e| is_not_hidden(e))
    13. .filter_map(|v| v.ok())
    14. .for_each(|x| println!("{}", x.path().display()));
    15. }

    九、在给定深度的目录,递归计算文件大小

            通过 WalkDir::min_depth 和 WalkDir::max_depth 方法,可以灵活设置目录的递归深度。下面的实例计算了 3 层子文件夹深度的所有文件的大小总和,计算中忽略根文件夹中的文件。

    1. use walkdir::WalkDir;
    2. fn main() {
    3. let total_size = WalkDir::new(".")
    4. .min_depth(1)
    5. .max_depth(3)
    6. .into_iter()
    7. .filter_map(|entry| entry.ok())
    8. .filter_map(|entry| entry.metadata().ok())
    9. .filter(|metadata| metadata.is_file())
    10. .fold(0, |acc, m| acc + m.len());
    11. println!("Total size: {} bytes.", total_size);
    12. }

    十、递归查找所有 png 文件

            在路径任意部分使用 ** 模式,例如,/media/**/*.png 匹配 media 及其子目录中的所有 PNG 文件。

    1. use error_chain::error_chain;
    2. use glob::glob;
    3. error_chain! {
    4. foreign_links {
    5. Glob(glob::GlobError);
    6. Pattern(glob::PatternError);
    7. }
    8. }
    9. fn main() -> Result<()> {
    10. for entry in glob("**/*.png")? {
    11. println!("{}", entry?.display());
    12. }
    13. Ok(())
    14. }

    十一、忽略文件名大小写,使用给定模式查找所有文件

            在 /media/ 目录中查找与正则表达模式 img_[0-9]*.png 匹配的所有图像文件。

            一个自定义 MatchOptions 结构体被传递给 glob_with 函数,使全局命令模式下不区分大小写,同时保持其它选项的默认值 Default。

            注:glob 是 glob command 的简写。在 shell 里面,用 * 等匹配模式来匹配文件,如:ls src/*.rs。

    1. use error_chain::error_chain;
    2. use glob::{glob_with, MatchOptions};
    3. error_chain! {
    4. foreign_links {
    5. Glob(glob::GlobError);
    6. Pattern(glob::PatternError);
    7. }
    8. }
    9. fn main() -> Result<()> {
    10. let options = MatchOptions {
    11. case_sensitive: false,
    12. ..Default::default()
    13. };
    14. for entry in glob_with("/media/img_[0-9]*.png", options)? {
    15. println!("{}", entry?.display());
    16. }
    17. Ok(())
    18. }

  • 相关阅读:
    facebook怎么加好友
    java-php-python-ssm-在线投稿系统-计算机毕业设计
    linux系统编程
    Discuz大气游戏风格模板/仿lol英雄联盟游戏DZ游戏模板GBK
    不容错过的用户标签全面解读。建议收藏!
    mac使用n切换node版本
    Vue.prototype则是一种注册全局变量的方式,使得定义的属性和方法可以在所有Vue实例中共享和访问。
    Python顺序、条件和循环
    黑客(网络安全)技术自学30天
    vue基础知识(三)
  • 原文地址:https://blog.csdn.net/xian0710830114/article/details/133563720