1. 关键词

C++ 文件系统操作 列出指定目录下的所有文件 列出指定目录下的所有文件夹 跨平台

2. fileutil.h


#pragma once

#include <string>
#include <cstdio>
#include <cstdint>
#include "filetype.h"
#include "filepath.h"

namespace cutl
{

    /**
     * @brief The file guard class to manage the FILE pointer automatically.
     * file_guard object can close the FILE pointer automatically when his scope is exit.
     */
    class file_guard
    {
    public:
        /**
         * @brief Construct a new file guard object
         *
         * @param file the pointer of the FILE object
         */
        explicit file_guard(FILE *file);

        /**
         * @brief Destroy the file guard object
         *
         */
        ~file_guard();

        /**
         * @brief Get the FILE pointer.
         *
         * @return FILE*
         */
        FILE *getfd() const;

    private:
        FILE *file_;
    };

    /**
     * @brief List all files in a directory.
     *
     * @param dirpath the filepath of the directory to be listed
     * @param type file type to be listed, default is all types.
     * @param recursive whether to list the whole directory recursively, default is false.
     * If true, means list the whole directory recursively, like the 'ls -R' command.
     * @return filevec the vector of file_entity, filepaths in the directory.
     */
    filevec list_files(const filepath &dirpath, filetype type = filetype::all, bool recursive = false);

} // namespace cutl
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

3. fileutil.cpp

#include <cstdio>
#include <map>
#include <iostream>
#include <cstring>
#include <sys/stat.h>
#include "fileutil.h"
#include "inner/logger.h"
#include "inner/filesystem.h"
#include "strutil.h"

namespace cutl
{
    file_guard::file_guard(FILE *file)
        : file_(file)
    {
    }

    file_guard::~file_guard()
    {
        if (file_)
        {
            // CUTL_DEBUG("close file");
            int ret = fclose(file_);
            if (ret != 0)
            {
                CUTL_ERROR("fail to close file, ret" + std::to_string(ret));
            }
            file_ = nullptr;
        }
        // ROBOLOG_DCHECK(file_ == nullptr);
    }

    FILE *file_guard::getfd() const
    {
        return file_;
    }

    filevec list_files(const filepath &dirpath, filetype type, bool recursive)
    {
        return list_sub_files(dirpath.str(), type, recursive);
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

4. filesystem_win.h


#include <vector>
#include <string>

#pragma once

namespace cutl
{
    filetype get_file_type(const std::string &filepath);
    filevec list_sub_files(const std::string &dirpath, filetype type = filetype::all, bool recursive = false);
} // namespace cutl
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

5. filesystem_win.cpp

#if defined(_WIN32) || defined(__WIN32__)

#include <io.h>
#include <direct.h>
#include <Windows.h>
#include <stdlib.h>
#include "strutil.h"
#include "filesystem.h"
#include "logger.h"

namespace cutl
{
    filetype get_file_type(DWORD attributes, const std::string &extension)
    {
        filetype type = filetype::unknown;

        if (attributes == INVALID_FILE_ATTRIBUTES)
        {
            CUTL_WARN("Failed to get file attributes, error code: " + std::to_string(GetLastError()));
            if (extension == ".lnk")
            {
                // 注意:测试时发现,有些快捷方式访问会失败,用后缀名判断进行兜底
                type = filetype::symlink;
            }
            return type;
        }
        else
        {
            if (attributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                type = filetype::directory; // directory
            }
            else if (attributes & FILE_ATTRIBUTE_NORMAL || attributes & FILE_ATTRIBUTE_READONLY)
            {
                // 普通文件|只读文件
                type = filetype::file; // regular file
            }
            else if ((attributes & FILE_ATTRIBUTE_ARCHIVE) && extension == ".lnk")
            {
                // windows的快捷方式
                type = filetype::symlink; // symbolic link
            }
        }

        return type;
    }

    std::string get_file_extension(const std::string &filepath)
    {
        auto pos = filepath.find_last_of('.');
        std::string extension = "";
        if (pos != std::string::npos)
        {
            extension = filepath.substr(pos);
            extension = cutl::to_lower(extension);
        }
        return extension;
    }

    // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesa
    // https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
    filetype get_file_type(const std::string &filepath)
    {
        auto attributes = GetFileAttributesA(filepath.c_str());
        auto extension = get_file_extension(filepath);
        // CUTL_DEBUG(filepath + ", extension: " + extension + ", attributes: " + std::to_string(attributes));
        return get_file_type(attributes, extension);
    }

    filevec list_sub_files(const std::string &dirpath, filetype type, bool recursive)
    {
        filevec file_list;

        // 使用FindFirstFileA查找第一个文件
        auto findpath = dirpath + win_separator + "*.*";
        WIN32_FIND_DATAA findData = {0};
        HANDLE hFind = FindFirstFileA(findpath.c_str(), &findData);
        if (hFind == INVALID_HANDLE_VALUE || hFind == NULL)
        {
            CUTL_ERROR("FindFirstFileA failed for " + findpath + ", errCode: " + std::to_string(GetLastError()));
            return file_list;
        }

        do
        {
            auto dwAttrs = findData.dwFileAttributes;
            auto filename = std::string(findData.cFileName);
            CUTL_DEBUG(filename + ", attributes: " + std::to_string(dwAttrs));
            if (is_special_dir(filename))
            {
                // “..”和“.”不做处理
                continue;
            }
            std::string filepath = dirpath + win_separator + filename;
            auto extension = get_file_extension(filename);
            auto ftype = get_file_type(dwAttrs, extension);
            if (ftype & type)
            {
                file_entity entity;
                entity.type = ftype;
                entity.filepath = filepath;
                file_list.emplace_back(entity);
            }
            if ((dwAttrs & FILE_ATTRIBUTE_DIRECTORY) && recursive)
            {
                // directory
                auto sub_files = list_sub_files(filepath, type, recursive);
                if (!sub_files.empty())
                {
                    file_list.insert(file_list.end(), sub_files.begin(), sub_files.end());
                }
            }
        } while (FindNextFileA(hFind, &findData));
        // 关闭句柄
        FindClose(hFind);

        return file_list;
    }
} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.

6. filesystem_unix.cpp

#if defined(_WIN32) || defined(__WIN32__)
// do nothing
#else

#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stack>
#include <cstring>
#include <utime.h>
#include <stdlib.h>
#include <sys/time.h>
#include "filesystem.h"
#include "inner/logger.h"

namespace cutl
{
    filetype get_file_type(const std::string &filepath)
    {
        struct stat file_stat;
        int ret = lstat(filepath.c_str(), &file_stat);
        if (0 != ret)
        {
            CUTL_ERROR("stat error. filepath:" + filepath + ", error:" + strerror(errno));
            return filetype::unknown;
        }

        return get_file_type(file_stat.st_mode);
    }
    filevec list_sub_files(const std::string &dirpath, filetype type, bool recursive)
    {
        filevec file_list;

        DIR *dir = opendir(dirpath.c_str()); // 打开这个目录
        if (dir == NULL)
        {
            CUTL_ERROR("opendir error. dirpath:" + dirpath + ", error:" + strerror(errno));
            return file_list;
        }
        struct dirent *file_info = NULL;
        // 逐个读取目录中的文件到file_info
        while ((file_info = readdir(dir)) != NULL)
        {
            // 系统有个系统文件,名为“..”和“.”,对它不做处理
            std::string filename(file_info->d_name);
            if (is_special_dir(filename))
            {
                continue;
            }
            struct stat file_stat; // 文件的信息
            std::string filepath = dirpath + unix_separator + filename;
            int ret = lstat(filepath.c_str(), &file_stat);
            if (0 != ret)
            {
                CUTL_ERROR("stat error. filepath:" + filepath + ", error:" + strerror(errno));
                closedir(dir);
                return file_list;
            }
            auto ftype = get_file_type(file_stat.st_mode);
            if (ftype & type)
            {
                file_entity entity;
                entity.type = ftype;
                entity.filepath = filepath;
                file_list.emplace_back(entity);
            }

            if (S_ISDIR(file_stat.st_mode) && recursive)
            {
                auto sub_files = list_sub_files(filepath, type, recursive);
                if (!sub_files.empty())
                {
                    file_list.insert(file_list.end(), sub_files.begin(), sub_files.end());
                }
            }
        }
        closedir(dir);

        return file_list;
    }
} // namespace cutl

#endif // defined(_WIN32) || defined(__WIN32__)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.

7. 源码地址

更多详细代码,请查看本人写的C++ 通用工具库:  common_util, 本项目已开源,代码简洁,且有详细的文档和Demo。


【SunLogging】

C++文件系统操作5 - 跨平台列出指定目录下的所有文件和文件夹_开发工具

扫码二维码,关注微信公众号,阅读更多精彩内容