How to Get the Contents of a Directory in C++

By John Paul Mueller, Jeff Cogswell

If you want to read the contents of a directory, you’re really going against what’s available in the standard C++ language. However, the Kind Souls of the Great Libraries of C++ (that is, the people who wrote most of the available C++ libraries) usually built in some handy functions for getting the contents of a directory.

A directory usually contains multiple files as well as other directories. Getting a list of contents is involved. You don’t just call a function and get something back.

Of course, if the Standard C++ Library included a function for getting information, it would likely be a template class that contains the directory contents. Alas, the library doesn’t support it. Instead, you have to climb through some functions. Here’s how it works.

  1. Call _findfirst(), passing it a pathname and a pattern for the files whose names you want to find.

    For example, pass *.* to get all files in the directory, or *.txt to get all files ending in .txt. Also pass it a pointer to a _finddata_t structure.

  2. Check the results of _findfirst().

    If _findfirst() returned –1, it didn’t find any files (which means you’re finished). Otherwise it fills the _finddata_t structure with the first file it found, and it will return a number that you use in subsequent calls to the various find functions.

  3. Look at the _finddata_t structure to determine the name of the file, and other information such as create date, last access date, and size.

  4. Call _findnext() and pass it the following values: the number returned from _findfirst() and the address of a _finddata_t structure

    If _findnext() returns –1, it found no more files; you can go to Step 5. Otherwise look at the _finddata_t structure to get the information for the next file found. Then repeat Step 4.

  5. Call _findclose() and pass it the number returned from _findfirst().

    You’re all finished.

Youch! That’s kind of bizarre, but it’s the way things used to be done in the old days of programming, before the mainstream languages developed such civilized features as classes and objects.

The GetDirectoryContents example shows how to implement this elegant, old-fashioned process.

#include <iostream>
#include <io.h>
#include <time.h>
#include <string>
using namespace std;
string Chop(string &str)
{
    string res = str;
    int len = str.length();
    if (str[len - 1] == 'r')
    {
        res.replace(len - 1, 1, ");
    }
    len = str.length();
    if (str[len - 1] == 'n')
    {
        res.replace(len - 1, 1, ");
    }
    return res;
}
void DumpEntry(_finddata_t &data)
{
    string createtime(ctime(&data.time_create));
    cout << Chop(createtime) << "t";
    cout << data.size << "t";
    if ((data.attrib & _A_SUBDIR) == _A_SUBDIR)
    {
        cout << "[" << data.name << "]" << endl;
    }
    else
    {
        cout << data.name << endl;
    }
}
int main()
{
    _finddata_t data;
    int ff = _findfirst ("../*.*", &data);
    if (ff != -1)
    {
        int res = 0;
        while (res != -1)
        {
            DumpEntry(data);
            res = _findnext(ff, &data);
        }
        _findclose(ff);
    }
    return 0;
}

You can see how main()follows the steps just outlined. And for each of the data structures, the function called DumpEntry() was used. The DumpEntry() function prints out the information about the file. Here’s what you should see when you run the application (the current directory entry, the parent directory entry, and the four directories containing other examples):

Sun Dec 15 19:40:00 2013        0       [.]
Sun Dec 15 19:40:00 2013        0       [..]
Sun Dec 15 19:40:00 2013        0       [DeleteDirectory]
Sun Dec 15 19:40:00 2013        0       [GetDirectoryContents]
Sun Dec 15 19:40:00 2013        0       [MakeDirectory]
Sun Dec 15 19:40:00 2013        0       [RenameFile]

Note how, in the DumpEntry() function, you’re testing whether the item is a directory. This is another old (but reliable) way to program: Check for the presence of one particular tiny little bit in the middle of the attrib member of the structure, like this:

if (data.attrib & _A_SUBDIR == _A_SUBDIR)
{
    cout << "[" << data.name << "]" << endl;
}

And finally, you’ll notice a strange function included called Chop(). That’s because the ctime() function — otherwise handy for formatting the time — adds a carriage return (or newline) to the end of the string it creates. So chop that off. Otherwise the information after the date has to start on the next line of text, which wasn’t desired.