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 系统上,此函数通常按需调用 unlink 和 rmdir,在 Windows 上则是 RemoveDirectoryW 和 DeleteFileW。
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 要求 replace、add 或 remove 中有且仅有一者被设置。
不抛出重载在错误时无特殊行动。
参数
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
}
}
相关推荐
- 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用户来说是非常重要的技...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)