Search Files Under a Directory in C++

Summary

In this post, I will introduce how to get a vector of files in a directory in C++. Instead of filesystem in boost or C++ 17, this method is purely based on C++ 11 and Unix platforms.

Details

The following codes are self-explained.

// safe path join in unix-based system
string path_join(const string& p1, const string& p2) {
    char sep = '/';
    string tmp = p1;

    if (p1[p1.size() - 1] != sep) { // Need to add a
        tmp += sep;                // path separator
        return(tmp + p2);
    }
    else
        return(p1 + p2);
}

// make sure files are either directories or files
// dfs to get all files
void get_files(const string &dir, vector<string> &all_files) {
    shared_ptr<DIR> directory_ptr(opendir(dir.c_str()), [](DIR* dir){ dir && closedir(dir); });
    struct dirent *dirent_ptr;
    if (!directory_ptr) {
        cout << "Error opening : " << strerror(errno) << dir << std::endl;
        return;
    }

    while ((dirent_ptr = readdir(directory_ptr.get())) != nullptr) {
        // get all the files in a directory, ignoring ".." and "."
        if (strcmp(dirent_ptr->d_name, ".") != 0 && strcmp(dirent_ptr->d_name, "..") != 0) {
            string curr_path = path_join(dir, string(dirent_ptr->d_name));
            // if it is a regular file
            if (dirent_ptr->d_type == DT_REG) {
                all_files.push_back(curr_path);
            } else { // a directory
                get_files(curr_path, all_files);
            }
        }
    }
}

shared_ptr

shared_ptr<DIR> directory_ptr(opendir(dir.c_str()), [](DIR* dir){ dir && closedir(dir); });

I will introduce smart_ptr in another post. The syntax here of shared_ptr basically passes a initial pointer whose type is DIR (a directory stream) and a lambda function which defines how to safely delete the pointer (close the directory stream).
See opendir and closedir.

dirent and readdir

See readdir. On Linux, the dirent structure is defined as follows:

struct dirent {
    ino_t          d_ino;       /* inode number */
    off_t          d_off;       /* offset to the next dirent */
    unsigned short d_reclen;    /* length of this record */
    unsigned char  d_type;      /* type of file; not supported
                                   by all file system types */
    char           d_name[256]; /* filename */
};

file type

We can use variable members d_type in dirent to see if the current file is a regular file or a diretory.

Leave a Reply

Your email address will not be published. Required fields are marked *