百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

c++ filesystem详解(2) - 目录

sinye56 2024-12-03 18:10 8 浏览 0 评论

我们来深入了解 filesystem目录相关部分。

1. 创建

创建目录注意:

a. 目录的父目录是否存在

b. 目录已经存在处理

1.1 函数



bool create_directory( const std::filesystem::path& p );

(1)

bool create_directory( const std::filesystem::path& p, std::error_code& ec ) noexcept;

(2)

bool create_directory( const std::filesystem::path& p, const std::filesystem::path& existing_p );

(3)

bool create_directory( const std::filesystem::path& p, const std::filesystem::path& existing_p, std::error_code& ec ) noexcept;

(4)

bool create_directories( const std::filesystem::path& p );

(5)

bool create_directories( const std::filesystem::path& p, std::error_code& ec );

(6)



1,2) 如同用 POSIX mkdir() 以 static_cast<int>(std::filesystem::perms::all) 为第二实参来创建目录 p(父目录必须已经存在)。若该函数因为 p 解析到既存目录而失败,则不报告错误。否则在失败时报告错误。

2) 同 (1,2),但新目录的属性复制自 existing_p(必须是已存在的目录)。复制的属性取决于操作系统:在 POSIX 系统上,如同按照下方复制属性

stat(existing_p.c_str(), &attributes_stat)
mkdir(p.c_str(), attributes_stat.st_mode)

在 Windows 操作系统上,不复制 existing_p 的属性。

5,6) 对每个尚未存在的 p 的元素执行 (1,2)。若 p 已存在,则函数不做任何事(不把此条件当做错误)。

参数

p

要创建的新目录的路径

existing_p

要自之复制属性的目录的路径

ec

不抛出重载中报告错误的输出形参

返回值

若创建了 p 所解析到的目录则为 true,否则为 false。

异常

若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。

1,5) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。

若 OS API 调用失败,则 @2,6@ 设置 std::error_code& 形参

为 OS API 错误码,而未发生错误时则执行 ec.clear()。

3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参,以 existing_p 为第二路径实参,并以OS 错误码为错误码实参。

若 OS API 调用失败,则 @4@ 设置 std::error_code& 形参

为 OS API 错误码,而未发生错误时则执行 ec.clear()。

注解

保持属性的重载 (3,4) 被 copy() 在递归地复制目录时隐式调用。其在 boost.filesystem 的等价物是 copy_directory(实参顺序相反)。

1. 2 示例

#include <iostream>
#include <string>
#include <filesystem>

namespace fs = std::filesystem;
int main() {
    
    //获取当前路径
    fs::path currentPath = fs::current_path();
    std::error_code ec;
    bool ret = false;

    //创建 tdir/sub 目录
    //create_directory 
    //创建因为要求父目录必须已经存在,
    //而(tdir不存在)所以下面必定失败
    fs::path path(currentPath);
    path.append("tdir/sub_tdir");
    std::cout << path << std::endl;

    try {
        //失败,抛异常,所以要处理
        fs::create_directory(path);
    }
    catch (fs::filesystem_error& e) {
        std::cout << "failed  ";
        std::cout << e.code() << " : " << e.what() << std::endl;
    }

    //失败不抛异常
    if (!fs::create_directory(path, ec) && ec) {
        std::cout << "failed  ";
        std::cout <<ec.value()<<" : " << ec.message() << std::endl;
    }

    //create_directories 
    //上级目录不存在,则创建
    if (!fs::create_directories(path, ec) && ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    std::cout << "ok" << std::endl;

    //目录已经存在,再创建, 会返回失败
    //这时需要判断ec
    if (!fs::create_directory(path, ec) && !ec) {
        std::cout << "目录已经存在" << std::endl;
    }
    if (!fs::create_directories(path, ec) && !ec) {
        std::cout << "目录已经存在" << std::endl;
    }
    std::cout << "done" << std::endl;

    //综上所述:
    //1、尽量不要使用抛异常函数
    //2、目录存在,函数返回false,
    //   要判断std::error_code
    
    return 0;
}

1.3 平台API

平台创建目录的API Windows

#include <windows.h>
BOOL
WINAPI
CreateDirectoryW(
    _In_ LPCWSTR lpPathName,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );

Linux

#include <sys/stat.h>
#include <sys/types.h>

int mkdir(const char *pathname, mode_t mode);

2. 删除

删除目录注意:

a. 非空目录怎样删除

2.1 函数



bool remove( const std::filesystem::path& p );

(1)

bool remove( const std::filesystem::path& p, std::error_code& ec ) noexcept;

(2)

std::uintmax_t remove_all( const std::filesystem::path& p );

(3)

std::uintmax_t remove_all( const std::filesystem::path& p, std::error_code& ec );

(4)



1,2) 删除路径 p 所标识的文件或空目录,如同用 POSIX remove。不跟随符号链接(移除符号链接,而非其目标)。

3,4) 递归地删除 p 的内容(若它是目录)及其所有子目录的内容,然后删除 p 自身,如同重复应用 POSIX remove。不跟随符号链接(移除符号链接,而非其目标)。

参数

p

要删除的路径

ec

不抛出重载中报告错误的输出形参

返回值

1,2) 若文件被删除则为 true,若文件不存在则为 false。接受 error_code& 实参的重载在错误时返回 false。

3,4) 返回被删除的文件及目录数量(可以是零,若用以起始的 p 不存在)。接受 error_code& 实参的重载在错误时返回 static_caststd::uintmax_t(-1)。

异常

若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。

1,3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。

若 OS API 调用失败,则 @2,4@ 设置 std::error_code& 形参

为 OS API 错误码,而未发生错误时则执行 ec.clear()。

注解

在 POSIX 系统上,此函数通常按需调用 unlinkrmdir,在 Windows 上则是 RemoveDirectoryWDeleteFileW

2.2 示例

#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
int main() {
    
    //获取当前路径
    fs::path currentPath = fs::current_path();
    std::error_code ec;
    bool ret = false;

    //创建 tdir/sub 目录
    fs::path path(currentPath);
    path.append("tdir/sub_tdir");
    if (!fs::create_directories(path, ec) && ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    //创建 0.txt ... 9.txt 文件
    for (int i = 0; i < 10; i++) {
        auto fPath(path);
        fPath.append(std::to_string(i) + ".txt");
        
        std::ofstream fp(fPath.generic_string(), std::ios::out);
        fp.write("hello world", 11);
        fp.close(); 
    }
    
    //remove 只能删除空目录,
    //所以这里失败
    if (!fs::remove(path, ec) && ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
    }

    //remove_all 删除目录及其下内容
    //所以这里会成功
    path = path.parent_path();//上级目录
    int files = fs::remove_all(path, ec);
    if (ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    std::cout << "ok remove files : " << files << std::endl;

    //综上所述:
    //1、尽量不要使用抛异常函数
    //2、删除非空目录,使用remove_all
    return 0;
}

3. 属性与权限

3.1 函数

  • 获取属性权限



std::filesystem::file_status status( const std::filesystem::path& p );

(1)

std::filesystem::file_status status( const std::filesystem::path& p, std::error_code& ec ) noexcept;

(2)

std::filesystem::file_status symlink_status( const std::filesystem::path& p );

(3)

std::filesystem::file_status symlink_status( const std::filesystem::path& p, std::error_code& ec ) noexcept;

(4)



1,2) 确定 p 所标识的文件系统对象的类型与属性,如同用 POSIX stat(符号链接跟随到其目标)。在下列描述中,prms 是 (m & perms::mask) 的结果,其中 m 如同通过从 POSIX struct stat 采用 st_mode 获得,并将其转换为类型 std::filesystem::perms。

3,4) 同 (1,2),但行为如同使用 POSIX lstat(不跟随符号链接):

参数

p

要检验的路径

ec

不抛出重载中报告错误的输出形参

返回值

文件状态(一个 filesystem::file_status 对象)

异常

若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。

1,3) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。

若 OS API 调用失败,则 @2,4@ 设置 std::error_code& 形参

为 OS API 错误码,而未发生错误时则执行 ec.clear()。

注解

此函数提供的信息通常也作为目录迭代的副产物提供,而且可能为 filesystem::directory_entry 的成员函数所获取。在目录迭代期间,不需要再次调用 status

  • 修改权限



void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::filesystem::perm_options opts = perm_options::replace );

(1)

void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::error_code& ec ) noexcept;

(2)

void permissions( const std::filesystem::path& p, std::filesystem::perms prms, std::filesystem::perm_options opts, std::error_code& ec );

(3)



更改 p 所解析的文件的访问权限,如同用 POSIX fchmodat。跟随符号链接,除非在 opts 中设置了 perm_options::nofollow

第二个签名表现如同以设为 perm_options::replace 的 opts 调用。

效果按如下方式依赖于 prms 与 opts:

  • 若 opts 是 perm_options::replace,则严格设置文件权限为 prms & std::filesystem::perms::mask 表示应用 prms 的每个有效位)。
  • 若 opts 是 perm_options::add,则严格设置文件权限为 status(p).permissions() | (prms & perms::mask)(表示每个设于 prms 却不在当前文件权限中的有效位被添加到文件权限)。
  • 若 opts 是 perm_options::remove,则严格设置文件权限为 status(p).permissions() & ~(prms & perms::mask)(表示每个 prms 中清除却设于当前文件权限中的有效位从文件权限被清除)。

opts 要求 replaceaddremove 中有且仅有一者被设置。

不抛出重载在错误时无特殊行动。

参数

p

要检验的路径

prms

要设置、添加或移除的权限

opts

控制此函数所采用行动的选项

ec

不抛出重载中报告错误的输出形参

异常

若内存分配失败,则任何不标记为 noexcept 的重载可能抛出 std::bad_alloc 。

1) 抛出 std::filesystem::filesystem_error,构造时以 p 为第一路径实参并以OS 错误码为错误码实参。

若 OS API 调用失败,则 @2,3@ 设置 std::error_code& 形参

为 OS API 错误码,而未发生错误时则执行 ec.clear()。

注解 权限不必用位实现,但概念上用这种方式处理它们。 某些系统上某些权限位可能被忽略,而更改某些位可能自动影响其他位(例如在无所有者/组/全体区分的平台上,设置三者任一部分都会写入三者全体)。

3.2 示例

#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
int main() {

    //获取当前路径
    fs::path currentPath = fs::current_path();
    std::error_code ec;
    bool ret = false;

    //创建 tdir/sub 目录
    fs::path path(currentPath);
    path.append("tdir/sub_tdir");
    if (!fs::create_directories(path, ec) && ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    std::cout << path << std::endl;

    fs::file_status st = fs::status(path, ec);
    if (ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    //类型
    //if(fs::is_directory(path))
    if (fs::file_type::directory == st.type()) {
        std::cout << "确实是目录" << std::endl;
    }

    //打印权限
    auto show = [](fs::perms ps) {
        std::cout << (fs::perms::none == (fs::perms::owner_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::owner_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::owner_exec & ps) ? '-' : 'x');
        std::cout << (fs::perms::none == (fs::perms::group_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::group_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::group_exec & ps) ? '-' : 'x');
        std::cout << (fs::perms::none == (fs::perms::others_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::others_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::others_exec & ps) ? '-' : 'x');
        std::cout << '\n';
    };
    //权限
    fs::perms ps = st.permissions();
    show(ps);

    //修改权限: 非所有者无权限
    ps = fs::perms::owner_all;
    if (fs::permissions(path, ps, fs::perm_options::replace, ec), ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }

    ps = fs::status(path, ec).permissions();
    show(ps);


    //remove_all 删除目录及其下内容
    //所以这里会成功
    path = path.parent_path();//上级目录
    int files = fs::remove_all(path, ec);
    if (ec) {
        std::cout << "failed  ";
        std::cout << ec.value() << " : " << ec.message() << std::endl;
        return 0;
    }
    std::cout << "ok remove files : " << files << std::endl;

    //综上所述:
    //1、尽量不要使用抛异常函数
    return 0;
}

4. 遍历

  • 遍历目录

4.1 类



class directory_iterator

目录元素迭代器

class recursive_directory_iterator

递归目录元素迭代器

  • directory_iterator
  • 遍历目录元素directory_entry。 不遍历子目录。 迭代顺序是未指定的,但每个目录条目只被造访一次。 跳过特殊路径名 ...
  • class recursive_directory_iterator 遍历目录元素directory_entry,迭代顺序是未指定的,但每个目录条目只被造访一次。 遍历子目录。 迭代顺序是未指定的,但每个目录条目只被造访一次。 跳过特殊路径名 ...

4.2 示例

#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
int main() {

    //获取当前路径
    fs::path currentPath = fs::current_path();
    std::error_code ec;
    bool ret = false;

    //打印权限
    auto show = [](fs::perms ps) {
        std::cout << " ";
        std::cout << (fs::perms::none == (fs::perms::owner_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::owner_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::owner_exec & ps) ? '-' : 'x');
        std::cout << (fs::perms::none == (fs::perms::group_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::group_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::group_exec & ps) ? '-' : 'x');
        std::cout << (fs::perms::none == (fs::perms::others_read & ps) ? '-' : 'r');
        std::cout << (fs::perms::none == (fs::perms::others_write & ps) ? '-' : 'w');
        std::cout << (fs::perms::none == (fs::perms::others_exec & ps) ? '-' : 'x');
        };

    fs::path dir;

    //directory_iterator
    std::cout << "directory_iterator" << std::endl;
    for (const auto& entry : fs::directory_iterator(currentPath, ec)) {
        if (dir != entry.path().parent_path()) {
            dir = entry.path().parent_path();
            std::cout << "\n" << dir << ":" << std::endl;
        }
        std::cout << (entry.is_directory() ? "DIR" : "FILE");
        std::cout << " " << entry.path().filename();
        std::cout << " " << entry.file_size();
        show(entry.status().permissions());
        std::cout << std::endl;
    }

    //recursive_directory_iterator
    std::cout << "recursive_directory_iterator" << std::endl;
    for (const auto& entry : fs::recursive_directory_iterator(currentPath, ec)) {
        if (dir != entry.path().parent_path()) {
            dir = entry.path().parent_path();
            std::cout << "\n" << dir << ":" << std::endl;
        }
        std::cout << (entry.is_directory() ? "DIR" : "FILE");
        std::cout << " " << entry.path().filename();
        std::cout << " " << entry.file_size();
        show(entry.status().permissions());
        std::cout << std::endl;
    }

    //综上所述:
    //1、尽量不要使用抛异常函数
    return 0;
}

4.3 解析

for (const auto& entry : fs::directory_iterator(currentPath, ec)) {...}

上面的遍历目录代码有些难解, 我们来解析一下。

我们知道上面的语句在编译时会转化成下面类似的语句

{
    auto && __range = range-expression ;
    auto __begin = begin-expr ;
    auto __end = end-expr ;
    for ( ; __begin != __end; ++__begin)
    {
        range-declaration = *__begin;
        loop-statement
    }
}

下面我们就看directory_iterator源码(有删减)

按上面的转化, 大约可得:

{
    auto && __range = range-expression ;
    auto __begin = begin(directory_iter(path, ec)) ;
    auto __end = end(directory_iter()) ;
    for ( ; __begin != __end; ++__begin)
    {
        entry = *__begin;//const directory_entry& operator*()
        loop-statement
    }
}


c++ filesystem详解(1) - 概念

相关推荐

Linux中10大常用命令之sort使用案例

请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言Linux中的sort命令用于对文本文件的内容进行排序。本教程向您展示了sort...

java开发常用的Linux命令,高频的没你想象的多

Linux的命令非常多,多到有些使用的场景你工作两三年也没有遇到过,工作三四年才能遇到(Linux内核开发,Shell脚本开发,嵌入式开发、、、),但这个不是今天分享的重点,今天分享的重点是Java开...

linux常用命令(收藏版)

linux小白注意啦,给大家分享一点干货,请笑纳!1.关机命令shutdown-hnow关闭系统(1)init0关闭系统(2),0为系统的进程号telinit0关闭系统(3)shutdo...

延续Win10三年需付超3000元!微软彻底封堵:删除绕过Win11系统要求教程、将第三方工具标记为恶意软件

一切都是为了用户能够正规地升级到Windows11。整理|屠敏出品|CSDN(ID:CSDNnews)距离Windows10退役仅剩8个月,微软最近这段时间,终是忍不住接连出手了...

敲完就让你提桶跑路的Linux命令

不谨慎可能就会让你提桶的Linux命令!!!删除文件rm-rf该命令是删除文件或文件夹等最快的方式之一。删除后的内容很难恢复,如果删除系统文件可能会导致系统崩坏。˃rm-rf/#强制删除根...

超级蠕虫,累计感染40万台服务器,让Linux内核服务器感染两年

最近著名安全公司ESET发布安全报告,报告分析了其对一个超级蠕虫Ebury的15年追踪分析。在15年中该病毒持续感染了40万台服务器,曾经在2011年(2009年)攻克了Linux内核维护站点kern...

linux redhat破解密码

适用于RedhatCentosFedora1.开机选择第一个启动项,按e进入编辑模式2.在启动项编辑模式找到linux16开头的文件,按ctrl+e快速定位到该行的行末,输入空格rd.break...

慎用!Linux最危险的10个命令!

Linux是一个强大而灵活的操作系统,它提供了许多功能丰富的命令和工具,让用户可以方便地管理和控制系统。但是,有些命令如果不小心或不知情地使用,可能会造成严重的后果,甚至导致系统崩溃或数据丢失。因此,...

Linux文件和目录删除

今天只讲一个命令,这个命令已经让万千运维人既爱又恨。rm删除文件或者目录基本用法:-i显示删除提示信息-f强制删除文件-r进行目录的递归删除在公司里为了保证数据安全,一般会创建一个alias...

给你的Linux系统穿上“防弹衣”:安全加固全攻略

为什么Linux系统需要安全加固在当今数字化时代,Linux系统以其开源、稳定、高效等特性,在服务器领域占据着举足轻重的地位。无论是大型互联网公司的核心业务,还是中小企业的日常运营,都离不开L...

一天一个Linux命令:文件操作「删」rm

命令:rm-rf文件名(慎用,慎用,慎用)rm(选项)(参数)命令功能:rm-rf是一条UNIX系统下的文件删除命令,作用是无提示地强制递归删除一个目录中的一个或多个文件或目录,如果没有使用...

Linux下通过 rm -f 删除大量文件时报错:Argument list too long

问题现象云服务器ECSLinux下通过rm-f删除大量的小文件时出现类似如下错误信息:-bash:?/bin/rm:?Argument?list?too?long如下图所示:问题原因如?待删...

这10个Linux命令太危险,千万慎用!数据毁灭的瞬间只需一个回车

你好,这里是网络技术联盟站,我是瑞哥。Linux系统,以其开源自由的特性,吸引了无数开发者和科技爱好者。其强大的命令行工具赋予了用户前所未有的控制能力。然而,正如俗话所说,“能力越大,责任越大”。某些...

Linux的10大危险命令,用过的运维都很刑

rm-rf命令该命令可能导致不可恢复的系统崩坏。˃rm-rf/#强制删除根目录下所有东西。˃rm-rf*#强制删除当前目录的所有文件。˃rm-rf.#强制删除当前...

Linux环境变量设置与查看全攻略

Linux环境变量设置与查看全攻略在Linux系统中,环境变量是用于定义系统和用户级设置的一种方法,它可以影响程序的行为和系统的运行方式。了解如何设置和查看环境变量对于Linux用户来说是非常重要的技...

取消回复欢迎 发表评论: