一、filesystem 介绍
filesystem 源自boost.filesystem库,在C++17合并进C++标准库中,filesystem中的所有操作是线程不安全的。
二、路径相关操作
在filesystem库中提供path类来对路径进行操作,后续的相关操作,如打开文件、遍历目录、判断文件类型等,都是需要用path作为参数来指定操作具体路径。
2.1 path 初始化
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path p; //empty path
fs::path p1 = "/usr/lib/sendmail.cf"; // portable format
fs::path p2 = p1; // copy construct
fs::path p3 = std::move(p1); // move construct
std::cout << "p = " << p << '\n'
<< "p1 = " << p1 << '\n'
<< "p2 = " << p2 << '\n'
<< "p3 = " << p3 << '\n';
}
output
p = ""
p1 = "" //被移动后p1 变成空路径
p2 = "/usr/lib/sendmail.cf"
p3 = "/usr/lib/sendmail.cf"
2.2 path 路径拼接操作
常见操作目录路径+文件名拼接成完成文件路径
path += 和 / 操作
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path p1 = "/usr/lib/";
fs::path p2 = "/usr/lib";
fs::path p3 = p1 / "text.txt";
fs::path p4 = p2 / "text.txt"; //会添加反斜杠
p1 += "text.txt";
p2 += "text.txt"; //不会添加反斜杠,操作更类似字符串拼接
std::cout << "p1 = " << p1 << '\n'
<< "p2 = " << p2 << '\n'
<< "p3 = " << p3 << '\n'
<< "p3 = " << p4 << '\n';
}
output-注意p2的路径输出是没有/usr/lib和text.txt之间是没反斜杆的
p1 = "/usr/lib/text.txt"
p2 = "/usr/libtext.txt"
p3 = "/usr/lib/text.txt"
p3 = "/usr/lib/text.txt"
2.3 对路径文件名进行操作修改
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path p = "foo/bar/old.txt";
//判断是否存在文件名
if(p.has_filename())
std::cout << p.filename() << std::endl; //text.txt
//替换文件名
p.replace_filename("new.txt");
std::cout << p.filename() << std::endl;
//删除文件名
p.remove_filename();
std::cout << std::boolalpha << p.has_filename() << std::endl; //false
return 0;
}
output:
"old.txt"
"new.txt"
false
2.4 对文件拓展名修改
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path p = "foo/bar/old.txt";
fs::path p1 = "foo/bar";
std::cout << std::boolalpha << p.has_extension()
<< " " << p1.has_extension() << std::endl;
//获取拓展名
std::cout << "p extension " << p.extension() << std::endl;
//替换拓展名
p.replace_extension("png");
std::cout << "replace png " << p << std::endl;
//删除拓展名
p.replace_extension("");
std::cout << "del extension " << p << std::endl;
//添加拓展名
p.replace_extension(".txt");
std::cout << "add extension .txt "<< p << std::endl;
return 0;
}
2.5 path 判断路径是否存在
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
const fs::path sandbox{"sandbox"};
fs::create_directory(sandbox);
std::ofstream{sandbox/"file"}; // create regular file
std::cout << std::boolalpha << fs::exists(sandbox) << std::endl; //true 目录路径
std::cout << std::boolalpha << fs::exists(sandbox/"file") << std::endl; //true 文件路径
//不存在路径
std::cout << std::boolalpha << fs::exists("/asd/asda") << std::endl;//false
fs::remove_all(sandbox);
}
三、目录相关操作
3.1 创建目录和删除目录
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
//设置当前路径 为 /tmp
//相当于 cd /tmp
fs::current_path(fs::temp_directory_path());
//mkdir -p sandbox/1/2/a 递归创建目录
fs::create_directories("./sandbox/1/2/a");
//mkdir sandbox/1/2/b 创建目录
fs::create_directory("./sandbox/1/2/b");
fs::permissions("./sandbox/1/2/b", fs::perms::others_all, fs::perm_options::remove);
//复制目录属性
fs::create_directory("./sandbox/1/2/c", "./sandbox/1/2/b");
std::system("ls -l sandbox/1/2");
std::system("tree sandbox");
//rm -rf sandbox
fs::remove_all("sandbox");
}
output
total 12
drwxr-xr-x 2 cyy cyy 4096 Dec 19 13:09 a
drwxr-x--- 2 cyy cyy 4096 Dec 19 13:09 b
drwxr-x--- 2 cyy cyy 4096 Dec 19 13:09 c
sandbox
└── 1
└── 2
├── a
├── b
└── c
5 directories, 0 files
3.2 遍历指定目录所有内容,不包含"."和".."
int main()
{
const std::filesystem::path sandbox{"sandbox"};
std::filesystem::create_directories(sandbox/"dir1"/"dir2");
std::ofstream{sandbox/"file1.txt"};
std::ofstream{sandbox/"file2.txt"};
//非递归遍历该目录下的内容
std::cout << "directory_iterator:\n";
// directory_iterator can be iterated using a range-for loop
for (auto const& dir_entry : std::filesystem::directory_iterator{sandbox})
std::cout << dir_entry.path() << '\n';
//递归遍历该目录下所有内容
std::cout << "\nrecursive_directory_iterator:\n";
for (auto const& dir_entry : std::filesystem::recursive_directory_iterator{sandbox})
std::cout << dir_entry << '\n';
// delete the sandbox dir and all contents within it, including subdirs
// 递归删除该目录所有内容 相当于 rm -rf sandbox
std::filesystem::remove_all(sandbox);
}
output
directory_iterator:
"sandbox/dir1"
"sandbox/file1.txt"
"sandbox/file2.txt"
recursive_directory_iterator:
"sandbox/dir1"
"sandbox/dir1/dir2"
"sandbox/file1.txt"
"sandbox/file2.txt"
四、文件类型判断
在filesystem库中使用枚举类型file_type对文件类型进行定义
常量 | 含义 |
---|---|
none | 文件状态尚未求值,或求值时发生错误 |
not_foud | 找不到该文件 |
regular | 常规文件(二进制、文本等) |
directory | 目录 |
symlink | 符号链接 |
block | 块(设备) |
character | 块(设备) |
fifo | 管道类型文件 |
socket | socket |
unknown | 文件存在但类型不确定 |
测试用例
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
namespace fs = std::filesystem;
void demo_status(const fs::path& p, fs::file_status s)
{
std::cout << p;
switch (s.type())
{
case fs::file_type::none:
std::cout << " has `not-evaluated-yet` type";
break;
case fs::file_type::not_found:
std::cout << " does not exist";
break;
case fs::file_type::regular:
std::cout << " is a regular file";
break;
case fs::file_type::directory:
std::cout << " is a directory";
break;
case fs::file_type::symlink:
std::cout << " is a symlink";
break;
case fs::file_type::block:
std::cout << " is a block device";
break;
case fs::file_type::character:
std::cout << " is a character device";
break;
case fs::file_type::fifo:
std::cout << " is a named IPC pipe";
break;
case fs::file_type::socket:
std::cout << " is a named IPC socket";
break;
case fs::file_type::unknown:
std::cout << " has `unknown` type";
break;
default:
std::cout << " has `implementation-defined` type";
break;
}
std::cout << '\n';
}
int main()
{
// create files of different kinds
fs::create_directory("sandbox");
fs::create_directory("sandbox/dir");
std::ofstream{"sandbox/file"}; // create regular file
fs::create_symlink("file", "sandbox/symlink");
mkfifo("sandbox/pipe", 0644);
sockaddr_un addr;
addr.sun_family = AF_UNIX;
std::strcpy(addr.sun_path, "sandbox/sock");
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof addr);
// demo different status accessors
for (auto it{fs::directory_iterator("sandbox")}; it != fs::directory_iterator(); ++it)
demo_status(*it, it->symlink_status()); // use cached status from directory entry
demo_status("/dev/null", fs::status("/dev/null")); // direct calls to status
demo_status("/dev/sda", fs::status("/dev/sda"));
demo_status("sandbox/no", fs::status("/sandbox/no"));
// cleanup (prefer std::unique_ptr-based custom deleters)
close(fd);
fs::remove_all("sandbox");
}