C++ directory_iterator | Traverse folders with std::filesystem
이 글의 핵심
Practical guide to iterating directories: recursion, skip_permission_denied, symlink policies, visiting sets for cycles, and when to batch subtrees for parallelism.
What is directory_iterator?
Iterate directory entries (C++17).
#include <filesystem>
namespace fs = std::filesystem;
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << std::endl;
}
Recursive traversal
for (const auto& entry : fs::recursive_directory_iterator(".")) {
std::cout << entry.path() << std::endl;
}
directory_iterator vs recursive_directory_iterator
directory_iterator | recursive_directory_iterator | |
|---|---|---|
| Scope | Immediate children only | Full subtree |
| Default | Does not descend | Descends automatically |
| Options | directory_options | Same family + disable_recursion_pending |
| Typical use | List one level | Full-tree search, size totals |
There is no standard max-depth flag. Implement depth limits with manual recursion/queues, or call disable_recursion_pending() to skip entering specific directories (e.g. build).
for (fs::recursive_directory_iterator it(root), end; it != end; ++it) {
if (it->is_directory() && it->path().filename() == "build") {
it.disable_recursion_pending();
}
}
Practical examples
Example 1: List files
void listFiles(const fs::path& dir) {
for (const auto& entry : fs::directory_iterator(dir)) {
if (entry.is_regular_file()) {
std::cout << entry.path().filename() << std::endl;
}
}
}
Example 2: Find extension
std::vector<fs::path> findFiles(const fs::path& dir,
const std::string& ext) {
std::vector<fs::path> result;
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
if (entry.is_regular_file() &&
entry.path().extension() == ext) {
result.push_back(entry.path());
}
}
return result;
}
Example 3: Directory size
uintmax_t calculateDirSize(const fs::path& dir) {
uintmax_t size = 0;
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
if (entry.is_regular_file()) {
size += entry.file_size();
}
}
return size;
}
Example 4: Filter by size
void filterFiles(const fs::path& dir) {
for (const auto& entry : fs::directory_iterator(dir)) {
if (entry.is_regular_file()) {
auto size = entry.file_size();
if (size > 1024 * 1024) {
std::cout << entry.path() << ": "
<< size << " bytes" << std::endl;
}
}
}
}
Filtering patterns
The standard has no glob/regex filter—combine:
- Extension:
path::extension(),path::stem(). - Type:
is_regular_file(),is_directory(). - Exclude dirs: skip paths containing
.git,node_modules, etc. - Regex: apply to
path::string()if needed—mind Windows encodings.
With C++20 you can chain std::ranges adapters for cleaner filters.
Iterator options
fs::directory_iterator it(dir);
fs::directory_iterator it(dir,
fs::directory_options::follow_directory_symlink);
fs::directory_iterator it(dir,
fs::directory_options::skip_permission_denied);
Symlinks
- Directory symlinks: recursion may follow them—unexpectedly large trees or cycles.
follow_directory_symlink: explicitly follow; still no automatic cycle detection—track visited canonical paths on sensitive workloads.- Link vs target:
is_symlink(),symlink_statusvsstatusdistinguish link metadata from final type. - Hard links: the same inode through multiple paths can double-count size—deduplicate if required.
Advanced: search by name
#include <filesystem>
#include <string>
#include <vector>
namespace fs = std::filesystem;
std::vector<fs::path> find_by_name(const fs::path& root, std::string_view key) {
std::vector<fs::path> out;
auto opts = fs::directory_options::skip_permission_denied;
std::error_code ec;
for (const auto& e : fs::recursive_directory_iterator(root, opts, ec)) {
if (ec) break;
if (!e.is_regular_file()) continue;
auto fn = e.path().filename().string();
if (fn.find(key) != std::string::npos)
out.push_back(e.path());
}
return out;
}
Advanced: tree byte count
std::uintmax_t tree_bytes(const fs::path& p, std::error_code& ec) {
std::uintmax_t total = 0;
auto opts = fs::directory_options::skip_permission_denied;
for (const auto& e : fs::recursive_directory_iterator(p, opts, ec)) {
if (ec) break;
if (!e.is_regular_file()) continue;
std::error_code fe;
auto sz = fs::file_size(e.path(), fe);
if (!fe) total += sz;
}
return total;
}
error_code overloads
std::error_code ec;
for (const auto& entry : fs::directory_iterator("maybe_missing", ec)) {
if (ec) {
std::cerr << ec.message() << '\n';
break;
}
std::cout << entry.path() << '\n';
}
Combine with skip_permission_denied so permission errors on single entries do not kill the walk. Still check ec around file_size/status on critical paths.
Performance
- Avoid unnecessary deep recursion when you only need shallow scans.
- Cache
entry.path()in a local variable inside hot loops. - Syscalls:
directory_entrymay cache metadata—do not assume, but avoid redundant queries. - Parallelism: split by subtrees + thread pools—watch disk contention.
- Network drives: high latency—batch and tune cache policy.
Common pitfalls
Pitfall 1: Exceptions vs error_code
Use skip_permission_denied and error_code when you cannot afford exceptions on every unreadable folder.
Pitfall 2: Symlinks and cycles
Document traversal policy; add a visited set for risky trees.
Pitfall 3: Performance
Prefer entry.file_size() when available instead of extra fs::file_size(path) calls.
Pitfall 4: Modify while iterating
Undefined behavior if you delete entries as you iterate—collect paths first, then delete.
FAQ
Q1: What is directory_iterator?
A: C++17 facility to enumerate directory entries.
Q2: Recursive walks?
A: recursive_directory_iterator.
Q3: Errors?
A: Exceptions or error_code overloads; skip_permission_denied helps.
Q4: Performance?
A: Reuse path, minimize syscalls, consider parallel subtree walks.
Q5: Modify during iteration?
A: Avoid—collect then mutate.
Q6: Learning resources?
A: C++17 – The Complete Guide, C++ Primer, cppreference — directory_iterator.
Related posts (internal)
- C++ File Status
- C++ File Operations
- C++ Filesystem library
Practical tips
Debugging
- Fix warnings first.
- Reproduce on a small directory tree.
Performance
- Profile real trees.
Code review
- Follow conventions.
Production checklist
Before coding
- Correct traversal strategy?
- Symlink policy documented?
While coding
- Errors handled?
- Edge cases (permissions, races)?
At review
- Clear intent?
- Tests on sample trees?
Keywords
C++, directory_iterator, recursive_directory_iterator, filesystem, C++17, error_code
Related posts
- C++ File Operations |
- C++ File Status |
- C++ Filesystem quick reference |
- C++ Filesystem overview |
- C++ path |