Skip to content

Wiki

std::filesystem

About 10 min read

Existing English translations were reused. 8 current source blocks remain in Simplified Chinese, without triggering paid translation.

1.What problem does this section solve?

Before C++17, manipulating files and directories (traversing, creating, deleting, checking for existence) required relying on platform-specific APIs (such as POSIX's <dirent.h> and Windows's FindFirstFile), and the code could not be cross-platform.

std::filesystem providescross-platform file system operations; one codebase works on Linux, macOS, and Windows.

2.What is this feature?

std::filesystem 是 C++17 引入的文件系统库,提供了路径处理、文件/目录操作、遍历、权限查询等功能。核心类是 std::filesystem::path。

3.C++ Standard Edition

C++17

4.Required header files

#include <filesystem>

5.Basic syntax

namespace fs = std::filesystem;

fs::path p = "data/data.txt";  // 路径对象

// 检查文件
fs::exists(p);          // 是否存在
fs::is_regular_file(p); // 是否是普通文件
fs::is_directory(p);    // 是否是目录
fs::file_size(p);       // 文件大小

// 操作
fs::create_directory(p);     // 创建目录
fs::copy(src, dst);           // 拷贝文件
fs::remove(p);                // 删除文件/目录
fs::rename(old, new);         // 重命名

// 遍历
for (const auto& entry : fs::directory_iterator(p)) { ... }      // 非递归
for (const auto& entry : fs::recursive_directory_iterator(p)) {} // 递归

6.Linux 和 Windows 中的路径写法

上面的相对路径在两种系统中都可以使用;后面示例如果展示 /home/user/...,那是 Linux 的一次运行结果,Windows 会显示自己的盘符和路径。

Windows 绝对路径可以使用原始字符串,拼接目录时优先交给 path:

fs::path folder = R"(C:\work\data)";
fs::path file = folder / "result.txt";

相对路径以 fs::current_path() 为基准。fs::temp_directory_path() 可以取得当前系统的临时目录,不需要把 Linux 的 /tmp 写死。不同系统的权限和路径显示形式也可能不同,示例输出主要用于理解各字段的含义。

7.Common Operations

操作 说明
fs::exists(p) 路径是否存在
fs::is_directory(p) 是否目录
fs::is_regular_file(p) 是否普通文件
fs::create_directory(p) 创建目录
fs::create_directories(p) 递归创建目录(类似 mkdir -p)
fs::copy(src, dst) 拷贝文件
fs::remove(p) 删除文件或空目录
fs::remove_all(p) 递归删除(类似 rm -rf)
fs::rename(old, new) 重命名/移动
p.filename() 获取文件名
p.extension() 获取扩展名
p.parent_path() 获取父目录
p / "subdir" 路径拼接(用 / 运算符)

8.Example code

8.1.示例 1:检查文件/目录是否存在和大小的信息

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    // 用当前目录做演示
    fs::path current = fs::current_path();
    std::cout << "Current path: " << current << "\n";
    std::cout << "Exists: " << std::boolalpha << fs::exists(current) << "\n";
    std::cout << "Is directory: " << fs::is_directory(current) << "\n";

    // 获取路径的各部分
    std::cout << "Filename: " << current.filename() << "\n";
    std::cout << "Parent path: " << current.parent_path() << "\n";
    std::cout << "Root path: " << current.root_path() << "\n";

    return 0;
}

Run results(the path varies by machine):

Current path: "/home/user/MySource/my-blog"
Exists: true
Is directory: true
Filename: "my-blog"
Parent path: "/home/user/MySource"
Root path: "/"

8.2.Example 2: Based on Example 1, traverse all files in the directory

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path dir = fs::current_path();

    std::cout << "Contents of " << dir << ":\n";
    std::cout << "--------------------------------\n";

    // 遍历当前目录(不递归子目录)
    for (const auto& entry : fs::directory_iterator(dir))
    {
        std::string type = entry.is_directory() ? "[DIR] " : "[FILE]";
        std::cout << type << " " << entry.path().filename().string() << "\n";
    }

    return 0;
}

Running result(varies depending on directory contents):

Contents of "/home/user/MySource/my-blog":
--------------------------------
[DIR]  .git
[DIR]  app
[DIR]  content
[FILE] nuxt.config.ts
[FILE] package.json
[FILE] README.md
...

8.3.Example 3: On the basis of Example 2, filter files with specific extensions

#include <iostream>
#include <filesystem>
#include <vector>

namespace fs = std::filesystem;

// 查找指定目录下所有 .md 文件
std::vector<fs::path> find_md_files(const fs::path& dir)
{
    std::vector<fs::path> result;
    for (const auto& entry : fs::recursive_directory_iterator(dir))
    {
        if (entry.is_regular_file() && entry.path().extension() == ".md")
        {
            result.push_back(entry.path());
        }
    }
    return result;
}

int main()
{
    fs::path dir = fs::current_path() / "content";

    if (!fs::exists(dir))
    {
        std::cout << "Directory not found: " << dir << "\n";
        return 1;
    }

    auto md_files = find_md_files(dir);

    std::cout << "Found " << md_files.size() << " .md files:\n";
    for (const auto& f : md_files)
    {
        std::cout << "  " << f.filename().string() << "\n";
    }

    return 0;
}

Execution result(varies depending on file content):

Found 15 .md files:
  index.md
  ch2-前言.md
  2000-现代C++.md
  ...

8.4.Example 4: Based on Example 3, create directories, files, and copy

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

namespace fs = std::filesystem;

int main()
{
    fs::path test_dir = fs::temp_directory_path() / "cpp_test_demo";

    // 创建目录(如果已存在也不报错)
    if (fs::create_directory(test_dir))
    {
        std::cout << "Created: " << test_dir << "\n";
    }
    else
    {
        std::cout << "Already exists: " << test_dir << "\n";
    }

    // 创建一个文件
    fs::path file_path = test_dir / "hello.txt";
    {
        std::ofstream out(file_path);
        out << "Hello, filesystem!\n";
    }
    std::cout << "Created file: " << file_path << "\n";
    std::cout << "File size: " << fs::file_size(file_path) << " bytes\n";

    // 拷贝文件
    fs::path copy_path = test_dir / "hello_copy.txt";
    fs::copy(file_path, copy_path);
    std::cout << "Copied to: " << copy_path << "\n";

    // 检查两个文件都存在
    std::cout << "hello.txt exists: " << fs::exists(file_path) << "\n";
    std::cout << "hello_copy.txt exists: " << fs::exists(copy_path) << "\n";

    // 清理:删除测试目录
    fs::remove_all(test_dir);
    std::cout << "Cleaned up: " << test_dir << "\n";

    return 0;
}

Running result:

Created: "/tmp/cpp_test_demo"
Created file: "/tmp/cpp_test_demo/hello.txt"
File size: 21 bytes
Copied to: "/tmp/cpp_test_demo/hello_copy.txt"
hello.txt exists: true
hello_copy.txt exists: true
Cleaned up: "/tmp/cpp_test_demo"

9.Operation result

See the "run results" for each example above.

10.Key syntax explanation in the example

Example What it explains Newly introduced syntax Why it is written this way Notes
Example 1 Path information query fs::current_path()、exists()、filename() Get the current path and its components All path operations are cross-platform
Example 2 Traverse directory fs::directory_iterator Traverse all entries in the current directory Only traverses one level; does not recurse into subdirectories
Example 3 Recursive traversal and filtering fs::recursive_directory_iterator、extension() Recursively find files with a specific extension Recursive traversal can be slow; pay attention to directory depth
Example 4 Create, copy, delete create_directory()、copy()、remove_all() Common file operations, cross-platform remove_all Similar to rm -rf; use with caution

11.Exception-throwing version vs error_code version

Many filesystem functions have two usage patterns:

auto size = fs::file_size(path);      // 失败时抛异常

std::error_code ec;
auto size = fs::file_size(path, ec);  // 失败时不抛异常,错误写入 ec
Writing style Advantages Suitable scenarios
Exception-throwing version Code is shorter, automatically interrupts on failure The file should exist; failure is an exception
error_code version Does not throw exceptions, making it easier to continue processing Scanning a large number of files, uncertain permissions, allowing failed items to be skipped

11.1.Example 5: Use error_code to skip failed items when scanning a directory

#include <filesystem>
#include <iostream>
#include <system_error>

// filesystem 用来跨平台处理路径、文件和目录。
namespace fs = std::filesystem;

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    fs::path dir = fs::current_path();

    for (const auto& entry : fs::directory_iterator(dir))
    {
        std::error_code ec;
        auto size = fs::file_size(entry.path(), ec);

        if (ec)
        {
            std::cout << "[skip] " << entry.path().filename().string()
                      << " reason: " << ec.message() << "\n";
            continue;
        }

        std::cout << entry.path().filename().string()
                  << " size = " << size << "\n";
    }

    return 0;
}

Running result(varies depending on directory contents):

[skip] content reason: Is a directory
package.json size = 1234
README.md size = 2048

When scanning directories, encountering directories, insufficient permissions, and abnormal symbolic links is very common. Use error_code to allow the program to skip bad entries and continue running, rather than exiting on the first failure.

12.Common Errors

Error 1: Forgetting to check exists before operating directly

std::cout << fs::file_size("/nonexistent");  // ❌ 抛异常!

Correct approach: first exists() check, or use try-catch.

Error 2:create_directory and create_directories confusion

fs::create_directory("a/b/c");  // ❌ 如果 a/b 不存在则失败!

Correct approach:fs::create_directories("a/b/c"); will be created recursively.

Error 3: Using == to Compare Paths

if (path1 == path2)  // 只是字符串比较!

Path /home/user and /home/user/ may not be equal; use fs::equivalent(p1, p2) for canonical comparison.

Error 4: When linking, -lstdc++fs is required (old compilers)

In GCC 8 and earlier, you need to link the stdc++fs library:g++ -std=c++17 file.cpp -lstdc++fsGCC 9+ and Clang do not require it.

13.Usage Recommendations

  1. Use namespace fs = std::filesystem; to simplify code:this abbreviation is almost a community standard.
  2. Join paths with the / operator:auto p = base / "subdir" / "file.txt";,concise and cross-platform.
  3. When traversing large directories, use directory_iterator: avoid loading all files at once.
  4. Pay attention to exception safety in file operations:many filesystem functions throw fs::filesystem_error on failure.
  5. C++17's filesystem is basically sufficient:C++20/23 add some convenience functions, but the core remains unchanged.
  6. When scanning a large number of files, consider the error_code overload:avoid having one exception path interrupt the entire scan.

14.Summary

  • std::filesystem 提供跨平台的文件系统操作。
  • fs::path 是路径类,支持 / 拼接、拆分(filename()/extension()/parent_path())。
  • exists() 检查存在,create_directory() 创建目录,copy()/remove() 等操作文件。
  • directory_iterator 遍历目录,recursive_directory_iterator 递归遍历。
  • 操作前检查 exists(),注意异常处理。