Question
How can I retrieve the list of files in a directory from within C or C++ code?
I need to do this programmatically, and I cannot run the ls command and parse its output from inside my program.
Short Answer
By the end of this page, you will understand how directory listing works in C and C++, which APIs are commonly used, how to read directory entries safely, and how to handle practical issues such as skipping . and .., checking file types, and writing portable code.
Concept
In C and C++, listing files in a directory usually means using operating system APIs rather than a built-in language feature.
C itself does not provide a standard library function for reading directory contents. Instead, you typically use:
- POSIX directory functions on Unix-like systems such as Linux and macOS
std::filesystemin modern C++ for a higher-level, standard approach- Platform-specific APIs on Windows when needed
This matters because real programs often need to:
- process all files in a folder
- load configuration files
- scan logs or images
- build file indexes
- validate uploads or backups
The key idea is simple:
- Open a directory
- Read each entry one by one
- Ignore special entries if needed
- Build your own list or process entries immediately
- Close the directory
In modern C++, std::filesystem is usually the easiest and most readable option. In C, or in older C++ codebases, POSIX functions like opendir(), readdir(), and closedir() are common on Unix-like systems.
Mental Model
Think of a directory as a box of labeled items.
- Opening the directory is like opening the box.
- Reading entries is like pulling out one label at a time.
- Each label tells you the name of something inside.
- Some labels are special, like
.and.., which represent the current and parent directories. - Closing the directory is like putting the lid back on when you are done.
In modern C++, std::filesystem acts like a cleaner tool that lets you walk through the box without dealing with as many low-level details.
Syntax and Examples
C on Unix-like systems with opendir and readdir
#include <stdio.h>
#include <dirent.h>
int main(void) {
DIR *dir = opendir(".");
if (dir == NULL) {
perror("opendir");
return 1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
printf("%s\n", entry->d_name);
}
closedir(dir);
return 0;
}
What this does
opendir(".")opens the current directory.readdir(dir)returns one entry at a time.entry->d_nameis the file or directory name.closedir(dir)releases the directory handle.
C++17 with
Step by Step Execution
Consider this C example:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main(void) {
DIR *dir = opendir("test_folder");
if (dir == NULL) {
perror("opendir");
return 1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
printf("Found: %s\n", entry->d_name);
}
closedir(dir);
return 0;
}
Step by step
-
opendir("test_folder")
Real World Use Cases
Listing files in a directory is used in many practical situations:
- Batch processing: read all
.csvfiles in a folder and import them. - Media apps: scan image or video directories.
- Log analysis: find all log files generated today.
- Backup tools: iterate through files before copying them.
- Configuration loading: load all config files from a plugin folder.
- Build tools: detect source files automatically.
- Server applications: inspect uploaded files in a working directory.
Example in C++ for filtering by extension:
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
for (const auto& entry : fs::directory_iterator(".")) {
if (entry.is_regular_file() && entry.path().extension() == ".txt") {
std::cout << entry.path().filename().string() << '\n';
}
}
}
This pattern is common when processing only certain file types.
Real Codebase Usage
In real projects, developers rarely just print file names. They usually combine directory iteration with validation and filtering.
Common patterns
Guard clauses
Check early whether a directory can be opened.
DIR *dir = opendir(path);
if (dir == NULL) {
perror("opendir");
return 1;
}
This avoids deeply nested code.
Filtering entries
Developers often skip:
.and..- hidden files
- non-regular files
- files with the wrong extension
Process while iterating
Instead of storing all names first, many programs process each file immediately.
for (const auto& entry : fs::directory_iterator(folder)) {
if (!entry.is_regular_file()) {
continue;
}
// process file here
}
This can use less memory.
Error handling
File systems are messy. Files may disappear, permissions may change, or symbolic links may behave differently. Real code often checks for errors around each operation.
Common Mistakes
1. Forgetting to close the directory
Broken example:
DIR *dir = opendir(".");
if (dir == NULL) {
return 1;
}
// read entries
// forgot closedir(dir)
Why it is a problem:
- It leaks resources.
- Repeated calls may eventually cause failures.
Fix:
closedir(dir);
2. Assuming every entry is a regular file
Broken example:
for (const auto& entry : std::filesystem::directory_iterator(".")) {
std::cout << entry.path().filename().string() << '\n';
}
This lists files and directories. If you only want files, check first.
Fix:
for (const auto& entry : std::filesystem::directory_iterator(".")) {
if (entry.()) {
std::cout << entry.().().() << ;
}
}
Comparisons
| Approach | Language | Standardized | Platform scope | Ease of use | Notes |
|---|---|---|---|---|---|
opendir / readdir / closedir | C, C++ | POSIX, not ISO C | Unix-like systems | Medium | Common low-level approach |
std::filesystem::directory_iterator | C++17 | Yes, standard C++ | Cross-platform in modern compilers | Easy | Best choice for modern C++ |
Shelling out to ls | Any | No | Depends on shell and OS | Poor |
Cheat Sheet
C on Unix-like systems
#include <dirent.h>
DIR *dir = opendir(".");
if (dir == NULL) {
// handle error
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// use entry->d_name
}
closedir(dir);
Important points
opendir(path)opens a directory.readdir(dir)returns the next entry orNULL.entry->d_nameis the entry name.closedir(dir)must be called.- Skip
.and..when needed.
C++17
#include <filesystem>
namespace fs = std::filesystem;
for (const auto& entry : fs::directory_iterator()) {
}
FAQ
Can standard C list files in a directory by itself?
No. ISO C does not include a standard directory listing API. On Unix-like systems, you usually use POSIX functions such as opendir() and readdir().
What is the easiest way in modern C++?
Use std::filesystem::directory_iterator from C++17.
Why should I not parse ls output?
Because it is fragile, less portable, and can break on filenames with spaces or unusual characters.
How do I skip . and ..?
In C, compare entry->d_name to "." and ".." and skip them. In C++ std::filesystem, these are generally not what you work with directly in the same way.
Can I list only files and not directories?
Yes. In C++ use entry.is_regular_file(). In C, you may need additional checks depending on the platform.
Can I scan subdirectories too?
Yes. In C++ use std::filesystem::recursive_directory_iterator. In C, you would implement recursion manually.
Does this work the same on Windows?
Mini Project
Description
Build a small program that scans a directory and prints only .txt files. This demonstrates how to iterate through directory entries, filter by file type, and work with filenames safely in a practical task similar to log scanning or batch import jobs.
Goal
Create a program that reads a directory path and prints the names of all regular .txt files inside it.
Requirements
- Read a directory path from the program.
- Iterate through the directory entries.
- Ignore anything that is not a regular file.
- Print only files with the
.txtextension.
Keep learning
Related questions
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.
C printf Format Specifier for bool: How to Print Boolean Values
Learn how to print bool values in C with printf, why no %b/%B specifier exists, and the common patterns to print true/false or 0/1.
Calling C or C++ from Python: Building Python Bindings
Learn the quickest ways to call C or C++ from Python, including ctypes, C extensions, Cython, and binding tools with practical examples.