文件系统 - 文件与目录的 CRUD

C++17 引入的 std::filesystem 库中,文件和目录的增删改查(CRUD)操作变得极其简单且跨平台。不再需要依赖底层的 POSIX API(如 open, stat, unlink)或 Windows APICreateFile, DeleteFile),而是使用统一的 C++ 接口。

1. 创建与复制

在 C++17 std::filesystem 中,文件的增操作主要涉及文件/目录的创建、复制以及内容的写入。由于 C++ 文件系统库主要关注路径和元数据的管理,创建文件内容通常仍然依赖传统的fstream库,而创建目录结构则是 filesystem 的强项。

1.1 创建

创建操作主要分为:创建空目录、创建目录树(多级目录)、以及创建空文件。

  1. 创建目录

    1
    bool create_directory(const std::filesystem::path& p);
    • 功能:尝试创建一个由路径 p 指定的目录,如果父目录不存在,则会抛出异常。

    • 返回值

      • 如果目录创建成功,或者目录已经存在,返回 true
      • 如果创建失败(例如父目录不存在,或者路径指向一个文件而非目录),返回 false
    • 特点:它只能创建单级目录。父目录必须已经存在,否则失败。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <filesystem>
    #include <iostream>
    namespace fs = std::filesystem;

    int main()
    {
    // 创建单级目录 "data"
    bool created = fs::create_directory("data");
    if (created)
    {
    // 如果目录不存在且创建成功,created 为 true
    }

    // 再次尝试创建,返回 true (表示目录已存在)
    bool exists = fs::create_directory("data");
    std::cout << "exists : " << exists << std::endl; // false

    return 0;
    }
  2. 创建多级目录

    1
    bool create_directories(const std::filesystem::path& p);
    • 功能:创建路径 p 指定的目录,如果路径中包含不存在的父目录,它会递归地自动创建所有缺失的父目录。

    • 返回值:

      • 如果目录被创建(包括创建的父目录),返回 true
      • 如果目录已经存在,返回 false
    • 注意:如果路径 p 已经是一个存在的目录,它什么也不做并返回 false

    示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <filesystem>
    #include <iostream>
    namespace fs = std::filesystem;

    int main()
    {
    // 创建单级目录 "data"
    bool created = fs::create_directories("a/b/c");
    if (created)
    {
    // 如果目录不存在且创建成功,created 为 true
    }

    // 再次尝试创建,返回 true (表示目录已存在)
    bool exists = fs::create_directories("a/b/c");
    std::cout << "exists : " << exists << std::endl;

    return 0;
    }
  3. 创建空文件

    std::filesystem 没有直接提供 create_file 函数。标准做法是利用传统的 std::ofstream 来创建一个空文件。示例代码如下:

    1
    2
    3
    4
    5
    6
    // 如果文件不存在,这会创建它;如果存在,这会截断它(清空内容)
    std::ofstream("config.ini").close();

    // 或者使用 C++17 的 fopen 风格
    std::FILE* fp = std::fopen("empty.txt", "w");
    if (fp) std::fclose(fp);

1.2 复制

C++17 提供了非常灵活的复制功能,支持递归复制目录、控制覆盖行为以及仅复制元数据。

  1. 拷贝文件

    1
    2
    3
    void copy(const std::filesystem::path& from, const std::filesystem::path& to);
    void copy(const std::filesystem::path& from, const std::filesystem::path& to, std::error_code& ec) noexcept;
    void copy(const std::filesystem::path& from, const std::filesystem::path& to, copy_options options);
    • 功能:将 from 复制到 to

    • 参数

      • 如果 from 是文件,则复制内容。
      • 如果 from 是目录,默认情况下不会递归复制内容(见下文 copy_options)。
      • copy_options 枚举:
        • none:默认行为。
        • skip_existing:如果目标已存在,跳过复制(不覆盖)。
        • overwrite_existing:如果目标已存在,覆盖它。
        • update_existing:如果目标已存在,仅当源文件比目标文件新时才覆盖。
        • recursive关键选项。递归复制子目录及其内容。
        • copy_symlinks:复制符号链接本身,而不是链接指向的文件。
        • directories_only:仅复制目录,不复制文件。
    • 异常:如果目标已存在,默认会抛出异常(除非使用特定的 copy_options 或者错误由 error_code 捕获)。

    • 示例:

      1
      2
      3
      4
      5
      // 将 file.txt 复制为 file_backup.txt,如果文件已存在则覆盖
      fs::copy("file.txt", "file_backup.txt", fs::copy_options::overwrite_existing);

      // 递归复制整个 "project" 文件夹到 "project_backup"
      fs::copy("project", "project_backup", fs::copy_options::recursive);
  2. 复制文件元数据

    如果我们只想复制文件的权限、修改时间等属性,而不想复制文件内容(或者内容已经复制完了,只想同步属性)可以使用copy_file函数:

    1
    void copy_file(const std::filesystem::path& from, const std::filesystem::path& to, copy_options options = copy_options::none);

    std::filesystem复制文件内容复制文件属性在概念上是区分的,但 copy_file 函数通常两者都做。如果你想只复制属性,通常需要手动读取并写入时间戳和权限,或者使用 copy 加上 skip_existing(但这不一定适用于属性同步,最标准的方式是用 permissionslast_write_time 函数单独设置)。

    下面是一个展示如何创建目录树、配置文件并将其备份的完整代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <iostream>
    #include <filesystem>
    #include <fstream>

    namespace fs = std::filesystem;

    void initialize_project(const std::string& project_name)
    {
    fs::path root = project_name;
    fs::path src = root / "src";
    fs::path config_file = root / "config.json";

    try
    {
    // 1. 创建多级目录结构
    if (fs::create_directories(src))
    {
    std::cout << "Created directories: " << src << std::endl;
    }

    // 2. 创建空文件 (并写入初始内容)
    std::ofstream(src / "main.cpp") << "#include <iostream>\nint main() {}";
    std::cout << "Created source file." << std::endl;

    std::ofstream(config_file) << "{ \"version\": 1.0 }";
    std::cout << "Created config file." << std::endl;

    // 3. 复制文件 (备份)
    fs::path backup_dir = root / "backup";
    fs::create_directory(backup_dir);

    // 将 config.json 复制到 backup/ 目录,如果存在则覆盖
    fs::copy_file(config_file, backup_dir / "config.json.bak",
    fs::copy_options::overwrite_existing);
    std::cout << "Created backup." << std::endl;

    }
    catch (const fs::filesystem_error& e)
    {
    std::cerr << "Error: " << e.what() << '\n';
    }
    }

    int main()
    {
    initialize_project("MyTestProject");
    return 0;
    }

2. 删除文件与目录

在 C++17 std::filesystem 中,删除操作(Delete)分为删除单个文件、删除空目录以及递归删除目录树。处理删除操作时,权限目标是否存在是需要特别注意的两个方面。

2.1 删除文件和空目录

删除文件是最基础的删除操作。使用的函数是remove:

1
2
bool remove(const std::filesystem::path& p);
bool remove(const std::filesystem::path& p, std::error_code& ec) noexcept;
  • 功能:删除路径 p 指向的文件或目录。
  • 返回值
    • 如果文件被成功删除,返回 true
    • 如果文件不存在,返回 false
    • 注意:如果 p 是一个非空目录,行为是未定义的(通常情况下会失败并返回 false,或抛出异常,具体取决于实现,但标准建议只用于删除文件或空目录)。
  • 异常:如果不使用 error_code 版本,失败时会抛出 filesystem_error 异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

void delete_file(const fs::path& p)
{
try
{
bool deleted = fs::remove(p);
if (deleted)
{
std::cout << "File deleted successfully.\n";
}
else
{
std::cout << "File does not exist or cannot be deleted.\n";
}
}
catch (const fs::filesystem_error& e)
{
std::cerr << "Error: " << e.what() << std::endl;
}
}

int main()
{
delete_file("MyTestProject/config.json");
return 0;
}

虽然 remove 可以删除空目录,但在语义上,有时我们需要明确仅当目录为空时才删除。严格来说,C++17 没有单独提供名为 remove_empty_directory 的函数。std::remove 就是删除空目录的标准函数。

remove(p)p 是目录时,只会在目录为空的情况下成功删除。并且remove 不会递归,如果目录里有东西,它会拒绝删除(失败)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fs::path dir_path = "temp_data";
if (fs::exists(dir_path) && fs::is_directory(dir_path))
{
// 尝试删除
// 如果目录里有文件,这个调用会返回 false 或抛出异常
if (fs::remove(dir_path))
{
std::cout << "Empty directory removed.\n";
}
else
{
std::cout << "Directory is not empty.\n";
}
}

2.2 删除所有内容

删除所有内容这是实际开发中最常用的删除函数,因为它足够智能。函数为remove_all:

1
2
uintmax_t remove_all(const std::filesystem::path& p);
uintmax_t remove_all(const std::filesystem::path& p, std::error_code& ec) noexcept;
  • 功能:递归地删除路径 p 指向的内容(包括所有子目录和文件)。
  • 行为
    • 如果 p 是一个文件,等同于 remove
    • 如果 p 是一个目录,它会删除该目录下的所有内容(递归),最后删除目录本身。
    • 如果 p 是一个符号链接,删除符号链接本身(不删除指向的目标)。
  • 返回值
    • 返回被删除的文件和目录的总数量
    • 如果 p 不存在,返回 0
  • 性能注意remove_all 需要遍历目录树,对于包含大量文件的深层目录,耗时可能较长。
1
2
3
4
5
// 无论 "build" 是一个文件、空目录还是包含成千上万文件的复杂目录结构
// 这一行代码都能将其彻底删除
namespace fs = std::filesystem;
uintmax_t count = fs::remove_all("build");
std::cout << "Deleted " << count << " items.\n";

下面是一个展示如何安全、优雅地处理删除场景的函数,包含错误处理和重试机制(针对 Windows 占用问题的一个简单模拟)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <filesystem>
#include <system_error>
#include <fstream>

namespace fs = std::filesystem;
bool safe_remove(const fs::path& p, bool recursive = true)
{
std::error_code ec;

if (recursive)
{
// remove_all 会递归删除,并且如果 p 不存在,返回 0,不抛异常
auto count = fs::remove_all(p, ec);
if (ec)
{
std::cerr << "Error deleting " << p << ": " << ec.message() << std::endl;
return false;
}
std::cout << "Removed " << count << " items at " << p << std::endl;
}
else
{
// 仅尝试删除单个文件或空目录
bool success = fs::remove(p, ec);
if (ec)
{
std::cerr << "Error deleting " << p << ": " << ec.message() << std::endl;
return false;
}
if (!success)
{
std::cout << "Path " << p << " does not exist (not an error)." << std::endl;
}
else
{
std::cout << "Removed " << p << std::endl;
}
}
return true;
}

int main()
{
// 创建测试结构
fs::create_directories("test_dir/subdir");
std::ofstream("test_dir/file.txt") << "content";

std::cout << " Unsafe remove failed (expected). Using remove_all instead.\n";
safe_remove("test_dir", true);

return 0;
}

3. 读取与遍历

在 C++17 std::filesystem 中,“查”主要包含三个层面的操作:

  1. 读取属性:获取文件的大小、权限、修改时间等元数据。
  2. 读取内容:读取文本文件内容(注:这通常仍结合传统流,但 std::filesystem 提供了路径支持)。
  3. 遍历目录:列出文件夹下的文件和子目录。

std::filesystem 的核心在于对文件属性的抽象,主要通过 file_status 和各种查询函数实现,这个在前文中已经做了非常细致的讲解 文件状态和属性

3.1 遍历目录

目录遍历这是 std::filesystem 最强大的功能之一,替代了老旧的 POSIX opendir / Windows FindFirstFile API。

3.1.1 单层遍历

单层目录遍历使用的类为std::filesystem::directory_iterator,构造函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1. 默认构造
// 创建一个 "end" 迭代器(指向空的 sentinel),不关联任何目录
directory_iterator() noexcept;

// 2. 拷贝/移动控制
directory_iterator(const directory_iterator& rhs);
directory_iterator(directory_iterator&& rhs) noexcept; // 移动构造 (C++17 后)
directory_iterator& operator=(const directory_iterator& rhs);
directory_iterator& operator=(directory_iterator&& rhs) noexcept;

// 3. 路径构造(核心构造函数)
// 显式构造:开始遍历 p 指向的目录
explicit directory_iterator(const std::filesystem::path& p);

// 带选项构造:例如 directory_options::skip_permission_denied (跳过无权限目录)
directory_iterator(const std::filesystem::path& p, directory_options options);

// 带错误码构造:若发生错误,不抛异常,而是将错误码写入 ec,迭代器变为 end
directory_iterator(const std::filesystem::path& p, directory_options options, std::error_code& ec) noexcept;

使用这个类可以遍历指定目录下的直接条目(不包括子目录中的文件)。示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void list_current_directory() 
{
for (const auto& entry : fs::directory_iterator("."))
{
// 检查是否是普通文件
if (entry.is_regular_file())
{
std::cout << "File: " << entry.path().filename()
<< " | Size: " << entry.file_size() << "\n";
}
// 如果是目录,你可以选择只打印名称,或者计算目录内文件的总大小(需要递归)
else if (entry.is_directory())
{
std::cout << "Dir : " << entry.path().filename() << "\n";
}
else
{
// 其他情况(如符号链接、设备文件等)可以视需求
// ...
}
}
}

3.1.2 递归遍历

std::filesystem::recursive_directory_iteratorC++17 标准库 <filesystem> 中提供的一个迭代器类,用于递归地遍历目录树及其所有子目录的内容。

1
2
3
4
5
6
recursive_directory_iterator() noexcept;
explicit recursive_directory_iterator(const std::filesystem::path& p,
std::filesystem::directory_options opts = directory_options::none);
explicit recursive_directory_iterator(const std::filesystem::path& p,
std::filesystem::directory_options opts,
std::error_code& ec) noexcept;
  • 默认构造函数:创建一个结束迭代器,通常用作遍历结束的判断条件。

  • 带路径的构造函数:

    • p: 要遍历的目录路径。

    • opts: 遍历选项(例如是否跳过符号链接、是否允许权限错误等)

      详细选项信息可查询在线文档
    • ec: 错误码,用于非抛出异常的重载。

在这个类中还为我们提供了一组用于控制递归层级的 API 函数:

  1. 跳过特定子目录

    1
    void pop();
    • 如果当前迭代器指向的是一个目录,调用 pop() 会跳过该目录下的所有内容,直接返回到该目录的父目录,并继续遍历。

    • 注意:这个函数的行为实际上是结束对当前目录的遍历,常用于跳过特定子目录。

    • 使用场景:已经进入了一个目录(或者正在深度遍历中),这是想强制停止当前的层级,直接回溯到上一层。

  2. 禁用递归进入

    1
    void disable_recursion_pending();
    • 这是一个非常实用的函数。当你遍历到一个目录时,如果调用此函数,迭代器在下一次递增时将不会进入该目录(尽管 directory_entry::is_directory() 返回 true),而是像对待普通文件一样跳过它。

    • 使用场景:在遍历列表时,发现一个目录,想在后续遍历时忽略它(把它当作普通文件处理),无进入。

  3. 深度查询

    1
    int depth() const;

    返回当前迭代器在目录树中的深度。起始目录深度为 0,其子目录为 1,以此类推。

递归遍历目录示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
std::string path = ".";

// 创建递归迭代器
for (auto it = fs::recursive_directory_iterator(path);
it != fs::recursive_directory_iterator(); ++it)
{
// 如果当前是一个目录且名字是 "skip_this_folder"
if (it->is_directory() && it->path().filename() == "skip_this_folder")
{
// 禁止递归进入,或者在进入后调用 it.pop()
it.disable_recursion_pending();
}

std::cout << it->path() << " (depth: " << it.depth() << ")\n";
}

return 0;
}

注意:recursive_directory_iterator 默认不会跟随符号链接进入子目录,以防止无限循环。如需跟随,需构造时传入 directory_options::follow_directory_symlink 选项。

3.2 过滤器与查找

C++17 文件系统库本身不提供 findfilter 函数,但利用迭代器和 C++ 算法库可以轻松实现。

示例:查找所有 .cpp 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <algorithm>
#include <vector>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

std::vector<fs::path> find_cpp_files(const fs::path& root)
{
std::vector<fs::path> cpp_files;

auto begin = fs::recursive_directory_iterator(root);
auto end = fs::recursive_directory_iterator();

// 使用 std::copy_if 配合 back_inserter
std::copy_if(begin, end, std::back_inserter(cpp_files), [](const fs::directory_entry& entry) {
// 只要常规文件,且扩展名为 .cpp (忽略大小写)
return entry.is_regular_file() &&
entry.path().extension() == ".cpp";
});

return cpp_files;
}

int main()
{
auto files = find_cpp_files("./");
for(const auto &it : files)
{
std::cout << it << std::endl;
}
return 0;
}

4. 文件修改

std::filesystem 中,修改的操作主要体现为对文件系统对象的重命名移动以及属性修改

4.1 重命名和移动

在底层系统中,重命名移动通常是同一个系统调用。只要源文件和目标文件在同一个文件系统(挂载点)上,移动操作仅仅是修改目录元数据,速度极快,且不涉及文件数据的物理拷贝。

1
2
bool rename(const std::filesystem::path& old_p, const std::filesystem::path& new_p);
bool rename(const std::filesystem::path& old_p, const std::filesystem::path& new_p, std::error_code& ec) noexcept;
  • 功能:
    • old_p 重命名为 new_p
    • 如果 new_p 路径中包含不同的目录名,则实现了移动效果。
  • 重要特性:
    • 覆盖行为:如果new_p已经存在,它的行为取决于编译器和操作系统。
      • 在大多数 POSIX 系统上,如果 new_p 是非空目录,会失败;如果是空目录或文件,通常原子性地替换。
      • 标准建议:如果 new_p 存在,结果是实现定义的。为了代码健壮性,强烈建议在移动前检查目标是否存在,或者确保期望覆盖。
    • 跨卷移动:如果源和目标在不同的磁盘分区(挂载点),单纯的 rename 可能会失败(错误码为 EXDEV)。标准的 rename 不执行跨文件系统的数据拷贝。

示例:重命名文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

void rename_file()
{
fs::path old_name = "data_old.txt";
fs::path new_name = "data_new.txt";

std::error_code ec;

// 检查旧文件是否存在
if (fs::exists(old_name))
{
fs::rename(old_name, new_name, ec);

if (ec)
{
std::cerr << "Rename failed: " << ec.message() << std::endl;
}
else
{
std::cout << "Successfully renamed to " << new_name << std::endl;
}
}
}

int main()
{
rename_file();
return 0;
}

示例:移动文件到新目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void move_file_to_folder() 
{
fs::path src = "report.pdf";
fs::path dest_folder = "archive/2023/";
fs::path dest = dest_folder / "report.pdf"; // 使用 / 运算符拼接路径

// 创建目标目录(如果不存在)
fs::create_directories(dest_folder);

std::error_code ec;
fs::rename(src, dest, ec);

if (ec)
{
// 这里的失败可能是跨磁盘移动失败
std::cerr << "移动失败: " << ec.message() << std::endl;
}
}

4.2 文件权限修改

修改文件的读写执行权限是“改”的另一重要部分。使用的函数如下:

1
2
void permissions(const std::filesystem::path& p, perms prms, perm_options opts = perm_options::replace);
void permissions(const std::filesystem::path& p, perms prms, std::error_code& ec) noexcept;
  • perms:枚举类型,如 owner_read, group_write, all_exec 等。

  • perm_options(重要):控制如何应用新权限,默认是replace(替换)。还可以使用:

    • add:在现有权限基础上添加(按位或)。
    • remove:在现有权限基础上移除(按位异或/取反)。
    • nofollow:如果是符号链接,不修改指向的目标,只修改链接本身(支持情况视系统而定)。
    std::filesystem::perm_options 属性在线查询

示例:将文件设置为只读

1
2
3
4
5
6
7
8
9
10
11
12
void make_readonly(const fs::path& p) 
{
// 使用 remove 选项移除写权限
// owner_all = 0700, group_all = 0070, others_all = 0007
// 我们要移除所有的 write 权限
fs::permissions(p,
fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write,
fs::perm_options::remove
);

std::cout << p << " is now read-only.\n";
}

5. 实战示例:清理工具

结合前面讲过的知识,我们可以写一个清理临时文件的小工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <filesystem>
#include <vector>
#include <chrono>

namespace fs = std::filesystem;

void clean_temp(const fs::path& dir)
{
// 1. 检查路径有效性
if (!fs::exists(dir) || !fs::is_directory(dir))
{
std::cerr << "Path is invalid or not a directory.\n";
return;
}

// 用于存储待删除的文件,避免在遍历过程中删除文件导致迭代器失效
std::vector<fs::path> files_to_remove;
try
{
auto options = fs::directory_options::skip_permission_denied;
for (const auto& entry : fs::recursive_directory_iterator(dir, options))
{
try
{
// 检查文件是否是临时文件
if (entry.is_regular_file() && entry.path().extension() == ".tmp")
{
// 3. 安全地获取时间,确保时钟类型一致
auto ftime = fs::last_write_time(entry);
// 使用 filesystem 自带时钟的 now()
auto now = fs::file_time_type::clock::now();

// 计算时间差 (7天)
auto duration = now - ftime;
if (duration > std::chrono::hours(24 * 7))
{
std::cout << "Marking for deletion: " << entry.path() << std::endl;
files_to_remove.push_back(entry.path());
}
}
}
catch (const fs::filesystem_error& e)
{
// 捕获单个文件处理时的错误(例如该文件刚好被其他进程占用无法读取元数据)
std::cerr << "Skipping file (Error): " << e.what() << std::endl;
continue;
}
}

// 4. 遍历结束后,统一执行删除操作
for (const auto& p : files_to_remove)
{
std::error_code ec; // 使用 error_code 避免异常抛出
bool removed = fs::remove(p, ec);
if (removed)
{
std::cout << "Successfully deleted: " << p << std::endl;
}
else
{
std::cerr << "Failed to delete " << p << ". Reason: " << ec.message() << std::endl;
}
}
}
catch (const fs::filesystem_error& e)
{
// 捕获迭代器层面的严重错误
std::cerr << "Filesystem critical error: " << e.what() << std::endl;
}
}

int main()
{
clean_temp("./test_temp");

return 0;
}