C++之路(1)filesystem
系统性介绍使用<filesystem>标准库来处理系统文件。

C++之路(1)filesystem

从C++17标准开始,C++新增加了<filesystem>标准库,用来处理系统文件。

#include <filesystem>

一、常用类

  • path类:该类只对字符串(路径)进行处理,是文件系统的基石。
  • file_status类:文件(或目录)属性(枚举类)。
  • directory_entry类:文件、文件夹(目录)。用path对象构造,构造前应提前判断 exists(path) 确保存在。
  • directory_iterator类:本级目录中的各文件(用于遍历目录)迭代器的容器,其元素为directory_entry对象。使用path对象构造,构造前应提前判断path是目录(file_type::directory)。
  • recursive_directory_iterator类:目录及其子目录中的各文件(用于遍历)迭代器的容器。

范例:

#include <filesystem>

using namespace std;
using namespace std::filesystem;

int main(){

	path pth("C:\\Windows");    // 构造path对象
	if (!exists(pth))		    // 必须先检测目录是否存在,才能使用文件入口
		return 1;
	directory_entry entry(pth); //构造文件入口
	
	if (entry.status().type() == file_type::directory) {/*...*/}    // 判断是否为目录。
	directory_iterator list(pth);   // 目录下所有文件的容器,path对象需要是目录。
	for (auto& it:list) { path p = it.path().filename() }    //通过it获取path对象的文件名
	system("pause");
	return 0;
}

二、详细介绍

(一)path

只做格式上处理,不做实质性判断,路径可能有误或不存在!

path 对象可以直接用 / 连接:path = path / "newfile"
path 对象用string构造,并可隐性转化为string

1. 拆分

只做字面拆分。返回的仍然是path对象。

  • root_path() Linux系统为"/";windows系统为"C:\"。等于 root_name() + root_directory() 注意:可能不指定,不存在!

    • root_name() Linux系统为空;windows系统为"C:"。注意:可能不指定,不存在。
    • root_directory() Linux系统为"/";windows系统为"\"。注意可能不存在。
  • relative_path() 除了root_path()之外(后)的。

  • filename() 最后一个/后边的全部;如果结尾是/,为空;如果path根本没有/,全部。特别注意:... 是filename,只不过有特殊含义。

  • stem() 不包括扩展名的文件名,filename()最后一个.(不含)之前的(即使还有.)。注意:如.在开头,作为stem的一部分。

  • extension() 扩展名,即filename()最后一个.(含)和其后的。

  • parent_path() 最后一个/(不含)之前的。即对于文件,所在目录;对于目录,上级目录。如果没有上级(或不知道上级),就为本身。

此外:

  • std::filesystem::current_path() 当前所在目录。(不给参数返回当前;给path参数修改切换)
    • 注意:文件处理时,时刻记得以当前目录为立足点,记得切换!
  • std::filesystem::temp_directory_path() 返回当前系统的TMP文件夹

注意: 这两个系统函数返回的path都以目录名收尾,不以 / 收尾。(自行构造时string收尾用 / 代表是目录)

2. 判断

返回bool

  • path.empty()
  • path.is_absolute() 检查root_path(),为空为false
  • path.is_relative() 等于is_absolute()取反。
  • path.has_filename() 判断filename()是否为空,path.has_???() 等其他的同理。

3. 规整规整

返回的仍然是path对象。

  • absolute(path) 返回绝对路径。如path本身是绝对路径,原封不动;如不是,等于字面上 current_path() + '/' + path,不做任何解析。如果path../abc,结果为/current/path/../abc
  • weakly_canonical(path) 返回规范的绝对路径,如果path为相对路径,则基于current_path()解析推导。例如path../abc,结果为/current/abc,注意这个地址可能不存在!如果path不规范、有歧义,则保留歧义,例如path如为 fff/abcabsolute(path)/current/path/fff/abc,而weakly_canonical(path)fff/abc,不基于 current_path()
  • canonical(path) 返回规范的绝对路径,并检查地址存不存在!如果地址不存在,抛出异常filesystem_error

(二)path定位的系统文件(或目录)属性

1. 取属性:

  • space(path) 返回地址的空间 filesystem::space_info 。其中:(单位均为bytes)
    • space_info.capacity 总容量
    • space_info.free 空闲容量
    • space_info.available 可用容量(小于等于空闲容量)
  • file_size(path) 返回文件大小,单位bytes
  • status(path) 返回filesystem::file_status。包括两项:
    • file_status.type 返回 filesystem::file_type 枚举类
      enum class file_type { none, not_found, regular, directory, symlink, unknown };
      // 有错、不存在、普通文件、目录、软连接、文件存在但不知道是啥
    • file_status.permissions 返回 filesystem::perms 枚举类
      enum class perms { none, all, owner_read, ……};
      // 0没设、0777(owner_all | group_all | others_all)、0400(作者只读)、……
    • 可赋值修改!(赋值时返回void)
enum class perms {
    none,         // 0 no permission bits are set

    owner_read,   // 0400 File owner has read permission
    owner_write,  // 0200 File owner has write permission
    owner_exec,   // 0100 File owner has execute/search permission
    owner_all,    // 0700 Equivalent to owner_read | owner_write | owner_exec

    group_read, group_write, group_exec, group_all,  // 040,020,010,070  The file's user group
    others_read, others_write, others_exec, others_all,  // 04,02,01,07  Other users

    all,   // 0777  All users. Equivalent to owner_all | group_all | others_all.
    
    mask,  // 07777 All valid permission bits.
}

2. 修改权限:

  • void permissions(path, perms, std::filesystem::perm_options)
    其中 perm_options
enum class perm_options {
    replace,  // 默认:completely replaced
    add,      // bitwise OR of the argument to the current permissions
    remove,   // bitwise AND of the negated argument to the current permissions
    
    nofollow  // permissions will be changed on the symlink itself, rather than on the file it resolves to.
};

3. 判断:(返回bool)

  • status_known(file_status s) 相当于 s.type() != file_type::none
  • exists(s) 相当于 status_known(s) && s.type() != file_type::not_found
  • exists(path) 相当于exists(status(path)),该版本新建status对象,效率低,尽量用file_status版。
  • is_directory(s/path) 两版本,相当于 s.type() == file_type::directory 。 注意:以不以 / 收尾均可准确判断。
  • is_regular_file(s/path)
  • is_empty(path) 是否为空文件或空目录

(三)系统文件(或目录)处理

1. 拷贝

  • void copy(from_path, to_path, copy_options) 拷贝文件或者目录。出错抛出异常。
    (默认行为:1重名抛异常;2拷贝目录只拷贝一层的文件,不包括子目录内容;3软链接拷贝其具体指向的文件;4内容拷贝。)
enum class copy_options {
    none,               # 默认的行为。
    
    skip_existing,
    overwrite_existing,
    update_existing,    # 只有当from的新时,才overwrtie
    
    recursive,          # 拷贝子目录及其内容
    
    copy_symlinks,      # 软链接也拷贝成软链接
    skip_symlinks,      # 跳过软链接,不拷贝
    
    directories_only,   # 只拷贝目录结构,不拷贝任何非目录文件
    create_symlinks,
    create_hard_links
};
// 例如:
const auto co1 = std::filesystem::copy_options::recursive;  // 修改一项默认行为
const auto co2 = update_existing | recursive | directories_only;  // 修改多项默认行为
  • bool copy_file(from_path, to_path, copy_options) 拷贝单一文件,返回bool,不抛异常。

2. 移动

  • void rename(old_path, new_path) 移动一个文件或者目录!(重命名只是原地移动的场景)。
    注意事项:(1)不能省略new_path目标的名字!(2)new_path是不存在的文件夹时,不能用 / 结尾!(3)new_pathfilename不能是 ... !(4)old_path是文件夹时,不能是new_path的上级目录!

    • old_path 不是文件夹,即移动文件时:
      • new_path 不是文件夹,且已存在:删除 new_path,再移动过来。
      • new_path 不存在,但parent_path存在:移动过来。
    • old_path 是文件夹,即移动文件夹时:
      • new_path是文件夹,且已存在:文件夹空时,删除(不空删除会报错!),再移动过来。
      • new_path不存在,且parent_path存在,且必须不以/收尾:移动。

3. 新建

  • bool create_directory(path) 创建新目录。parent directory必须存在,否则抛异常。如果path已经存在,返回false,不抛异常。
  • bool create_directories(path) 创建新目录。parent directory不用存在,一路新建。如果path已经存在,返回false,不抛异常。

4. 删除

  • bool remove(path) 删除一个文件或者空目录。path不存在,返回false
  • int remove_all(path) 删除一个文件或者一个目录及目录内全部内容,返回共删除的数量;path不存在,返回0

(三)遍历

1. directory_entry类(是迭代器的元素)

directory_entry对象用path构造,并可隐性转化为path
成员函数:path()
成员函数判断:existsis_directoryis_regular_filefile_size 等。

2. directory_iterator

遍历本级目录中的各文件的迭代器,其元素为directory_entry对象。遍历顺序未知,自动跳过 ... 文件。
使用 path 对象构造,注意需提前判断 path 是目录(file_type::directory)。

  • directory_iterator(path)
  • directory_iterator(path, std::filesystem::directory_options options)
    其中 option:
enum class directory_options {
    none,  // 默认:Skip directory symlinks, permission denied is error.
    follow_directory_symlink,  // Follow rather than skip directory symlinks.
    skip_permission_denied     // Skip directories that would otherwise result in permission denied errors.
};

使用:

for( auto& p : directory_iterator(path) ) {}

2. ecursive_directory_iterator

遍历目录及其子目录中的各文件的迭代器。
directory_iterator类 同理 。此外:

  • int depth() 迭代当前指向的文件夹的深度。起始文件夹为0,其子文件夹为1,以此类推。如果*thisend,结果未知!
  • bool recursion_pending() 如果下一个++ 还在本级目录中,返回true;如果下一个++ 进入子目录,返回false。

最后修改于 2024-02-24