C++ filesystem 库使用

发布时间 2023-12-25 20:42:46作者: 橙皮^-^

一、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");
}

四、参考