Spring Sale 2026 – NPI EA (cat = Baeldung on Linux)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 30% off until 31st March, 2026

>> EXPLORE ACCESS NOW

Baeldung Pro – Linux – NPI EA (cat = Baeldung on Linux)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

1. Overview

A common program task is listing files in a directory. To accomplish that, we need to jump inside the folder, read its content, and determine whether we have one or more files. To make this task comprehensive, we need to account for errors that can accompany each step.

In this tutorial, we’ll learn how to achieve this goal using the C and C++ programming languages.

2. The C Way

With C, we can open the directory using the opendir function. If it succeeds, the function provides a pointer to a stream of the folder’s items. So, we can read from this stream until it’s exhausted or an error occurs.

We can do that with the function readdir, which returns a pointer to the dirent structure. This structure contains details of the directory item, such as its name and type. Let’s study an example that lists files in the current folder:

#include <stdio.h>      /* for printf and stderr */
#include <string.h>     /* for strerror */
#include <dirent.h>     /* for DIR, opendir, readdir, and dirent */
#include <errno.h>      /* for errno */
#include <sys/stat.h>   /* for stat */

int main(int argc, char **argv)
{
    DIR*           FD;        /* represent the directory */
    struct dirent* in_file;   /* represent the file */
    char*          target_dir = "."; /* current directory */

    /* Scanning the target directory */
    FD = opendir(target_dir);
    if (FD == NULL) 
    {
        fprintf(stderr, "Error: Failed to open input directory - %s\n", strerror(errno));
        return 1;
    }

    /* Reading object (files, directories ...) from the folder */
    while ((in_file = readdir(FD)))
    {
        struct stat buffer;
        int         status;

        status = stat(in_file->d_name, &buffer);
        /* check status */
        if (status == -1)
        {
            fprintf(stderr, "Error: Failed to stat item - %s\n", strerror(errno));
            return 1;
        }
        /* check result */
        if ( buffer.st_mode & S_IFREG )
        {
            printf("%s is file \n", in_file->d_name);
        }
    }
    
    /* Close the directory */
    closedir(FD);
    
    return 0;
}

Notably, in the while loop condition (in_file = readdir(FD)), we simultaneously assign and check the in_file pointer. Next, we use the stat function to check if in_file refers to a file. This function fills in the buffer structure. Then, we check its st_mode field against the S_IFREG bit mask to detect a regular file.

We also examine errno, a value providing the last error number. In our example, we check it for the opendir and stat functions. If an error occurs, we print the error description returned by the strerror function to the standard error output.

3. The glibc Simplification

glibc is a GNU implementation of the C standard library. It adds the d_type field to the dirent structure to describe the item type. For a regular file, we check that this field has the value DT_REG:

#include <stdio.h>     /* for printf and stderr */
#include <string.h>    /* for strerror */
#include <dirent.h>    /* for DIR, opendir, readdir, and dirent */
#include <errno.h>     /* for errno */

int main(int argc, char **argv)
{
    DIR*           FD;
    struct dirent* in_file;
    char*          target_dir = ".";

    /* Scanning the target directory */
    FD = opendir(target_dir);
    if (FD == NULL)
    {
        fprintf(stderr, "Error : Failed to open target directory - %s\n", strerror(errno));
        return 1;
    }

    /* Reading object (files, directories ...) from the folder) */
    while ((in_file = readdir(FD)))
    {
        if (DT_REG == in_file->d_type)
        {
            printf("%s is file\n", in_file->d_name);
        }
    }
    
    /* Close the directory */
    closedir(FD);
    
    return 0;
}

It’s a good practice to close the directory stream using closedir(FD) after we’re done with it to release system resources.

4. The C++ Filesystem Library

Since the C++17 standard, we have a convenient filesystem library to interact with the filesystem. We use the path object to represent a directory. Then, we can iterate over the directory entries using directory_iterator and check their type with the is_regular_file function:

#include <filesystem>  // filesystem library
#include <iostream>    // std::cout, std::endl

namespace fs = std::filesystem;
int main()
{
    const fs::path target_path{"."};

    try {
        for (auto const& dir_entry : fs::directory_iterator{target_path})
        {
            if (fs::is_regular_file(dir_entry.path()))
            {
                std::cout << dir_entry.path().filename().string() << std::endl;
            }
        }
    }
    catch (fs::filesystem_error const& ex)
    {
        std::cout << "Error occurred during file operations!\n" << ex.what() << std::endl;
        return 1;
    }
    return 0;
}

We handle errors in a C++ manner, by catching the filesystem_error exception.

5. Conclusion

In this article, we learned how to read files in a directory using the C and C++ programming languages. In C we utilized the opendir and readdir functions to open a folder and read a stream of filesystem objects.

On the other hand, in C++ we iterated the folder content with the help of directory_iterator from the filesystem namespace. In both approaches, we checked if the retrieved item was a regular file. Finally, we accompanied all examples with rudimentary error checking.