11

how do I list subdirectories in windows using C++? Using code that would run cross-platorm is better.

avee
  • 766
  • 1
  • 13
  • 22
  • 4
    first, you search, then you try something and then post the code where your stuck. – Mitch Wheat May 26 '11 at 04:25
  • 4
    Cross platform? http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/index.htm – Etienne de Martel May 26 '11 at 04:25
  • 2
    possible duplicate of [Listing directory contents using C and Windows](http://stackoverflow.com/questions/2314542/listing-directory-contents-using-c-and-windows) – Adam Rosenfield May 26 '11 at 04:43
  • 1
    @Etienne : Why link to documentation for a version nearly two years old? Boost.Filesystem's API has changed significantly since then (v3 vs. v2), so it's worth linking to [current docs](http://www.boost.org/doc/libs/release/libs/filesystem/index.html). – ildjarn May 26 '11 at 06:02
  • @Etienne de Martel @ildjarn Thanks for the boost reference. It's very informative, but I want to use only POSIX standards if possible. – avee May 26 '11 at 07:36
  • 1
    @avee : You say you want a solution for Windows, preferably cross-platform, *and* adherent to POSIX standards? Make up your mind... ;-] (Really though, pick one.) – ildjarn May 26 '11 at 08:09
  • If you stick to POSIX, then you could always mess around with `dirent.h`. – Etienne de Martel May 26 '11 at 14:10
  • @ildjarn Well the first priority is code that runs in Windows, since the software is currently running in Windows. The second priority is cross-platform, POSIX standards code, since we are planning on migration to UNIX/Linux soon. We have always felt reluctant in using Boost, except the part that has become c++ standards. So.. the human mind is complicated ;p. @Etienne de Martel I'll try messing around with `dirent.h`, thanks! – avee May 27 '11 at 04:35
  • @avee : Boost.Filesystem v3 is in TR2, so if you consider TR1 (`shared_ptr<>`, `bind<>`, `function<>`, etc.) 'standard' then TR2 (and consequently Boost.Filesystem) should be considered 'standard' as well. – ildjarn May 27 '11 at 05:10

6 Answers6

8

Here's my solution for the problem, it is a Windows only solution though. I want to use a cross-platform solution, but not using boost.

#include <Windows.h>
#include <vector>
#include <string>

/// Gets a list of subdirectories under a specified path
/// @param[out] output Empty vector to be filled with result
/// @param[in]  path   Input path, may be a relative path from working dir
void getSubdirs(std::vector<std::string>& output, const std::string& path)
{
    WIN32_FIND_DATA findfiledata;
    HANDLE hFind = INVALID_HANDLE_VALUE;

    char fullpath[MAX_PATH];
    GetFullPathName(path.c_str(), MAX_PATH, fullpath, 0);
    std::string fp(fullpath);

    hFind = FindFirstFile((LPCSTR)(fp + "\\*").c_str(), &findfiledata);
    if (hFind != INVALID_HANDLE_VALUE)
    {
        do 
        {
            if ((findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
                && (findfiledata.cFileName[0] != '.'))
            {
                output.push_back(findfiledata.cFileName);
            }
        } 
        while (FindNextFile(hFind, &findfiledata) != 0);
    }
}

/// Gets a list of subdirectory and their subdirs under a specified path
/// @param[out] output Empty vector to be filled with result
/// @param[in]  path   Input path, may be a relative path from working dir
/// @param[in]  prependStr String to be pre-appended before each result
///                        for top level path, this should be an empty string
void getSubdirsRecursive(std::vector<std::string>& output, 
                         const std::string& path,
                         const std::string& prependStr)
{
    std::vector<std::string> firstLvl;
    getSubdirs(firstLvl, path);
    for (std::vector<std::string>::iterator i = firstLvl.begin(); 
         i != firstLvl.end(); ++i)
    {
        output.push_back(prependStr + *i);
        getSubdirsRecursive(output, 
            path + std::string("\\") + *i + std::string("\\"),
            prependStr + *i + std::string("\\"));
    }
}
camerondm9
  • 1,005
  • 15
  • 22
avee
  • 766
  • 1
  • 13
  • 22
  • thanx for the answer .. really helpful. –  Sep 12 '17 at 11:09
  • This algorithm doesn't detect folders with custom icon. – Mahdi-Malv Feb 19 '18 at 20:26
  • a way too buggy alg, sorry :) – Alexander Dyagilev Dec 14 '19 at 13:44
  • 1
    This algorithm seems to be doing recursion correctly, however the getSubdirs doesn't reliably detect whether the file is of directory type or not. The following if condition is not correct: if (findfiledata.dwFileAttributes | FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) Adjusting the problematic if condition to use the following one instead made the algorithm working reliably for me: if (findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) Please see https://learn.microsoft.com/en-us/windows/win32/fileio/listing-the-files-in-a-directory for more details – mbuster Jun 19 '20 at 11:59
4

http://msdn.microsoft.com/en-us/library/aa364418(v=vs.85).aspx

and

http://msdn.microsoft.com/en-us/library/aa365200(v=vs.85).aspx

its crossplatform between windows vista/7 or maybe xp :P

uray
  • 11,254
  • 13
  • 54
  • 74
  • The hidden trick is that GetFileAttributes will indicate whether a file is a directory or a regular file. Also, this solution will definitely work on XP. (And likely Windows 95 too). – selbie May 26 '11 at 04:38
  • @selbie You can also check the `WIN32_FIND_DATA.dwFileAttributes` part of the result from functions above for `FILE_ATTRIBUTE_DIRECTORY` value to check if the result is a directory. Also you should check for `.` and `..` as they are returned as results too. – avee May 26 '11 at 07:32
  • Chose this as the answer, since this is what I actually use in my solution. – avee May 27 '11 at 04:36
4

in 2022 onwards use

std::filesystem::directory_iterator

for example:

#include <filesystem>
using namespace std::filesystem;

path sandbox = "sandbox";
create_directories(sandbox/"dir1"/"dir2");

// directory_iterator can be iterated using a range-for loop
for (auto const& dir_entry : directory_iterator{sandbox}){
    if(dir_entry.is_directory()){ //checking if dir or file.
       std::cout << dir_entry << '\n';
    }
}

Keep in mind that there is also

std::filesystem::recursive_directory_iterator

which is used for traversing nested subdirectories, all in one go.

https://en.cppreference.com/w/cpp/filesystem/directory_iterator

Kari
  • 1,244
  • 1
  • 13
  • 27
  • 1
    Nowadays I use this solution too. An alternative when std::filesystem is not available is to use https://github.com/gulrak/filesystem. I've used it in production and it's very stable. – avee Aug 03 '22 at 03:17
4

Here's a relatively good solution that should work cross platform. You'll have to change the section of the code where you want it to do something but otherwise it should work quite well.

#include <cstring>
#include <io.h>
#include <iostream>
#include <stdio.h>

using namespace std;

void list(char* dir)
{
    char originalDirectory[_MAX_PATH];

    // Get the current directory so we can return to it
    _getcwd(originalDirectory, _MAX_PATH);

    _chdir(dir);  // Change to the working directory
    _finddata_t fileinfo;

    // This will grab the first file in the directory
    // "*" can be changed if you only want to look for specific files
    intptr_t handle = _findfirst("*", &fileinfo);

    if(handle == -1)  // No files or directories found
    {
        perror("Error searching for file");
        exit(1);
    }

    do
    {
        if(strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
            continue;
        if(fileinfo.attrib & _A_SUBDIR) // Use bitmask to see if this is a directory
            cout << "This is a directory." << endl;
        else
            cout << "This is a file." << endl;
    } while(_findnext(handle, &fileinfo) == 0);

    _findclose(handle); // Close the stream

    _chdir(originalDirectory);
}

int main()
{
    list("C:\\");
    return 0;
}

This is pretty concise code and will list all sub-directories and files in the directory. If you want to have it list all the contents in each sub-directory, you can recursively call the function by passing in the sub-directory on the line that prints out "This is a directory." Something like list(fileinfo.name); should do the trick.

legacybass
  • 582
  • 1
  • 7
  • 20
1

Look at Boost.Filesystem. It's cross-platform and free.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
-1

You can use the dirent bib. More details are here

  • Would you mind including the relevant parts of the linked article in your answer? Links can change making your answer obsolete and useless to future readers. See also [answer]. – CMW Dec 04 '13 at 20:03