首页 > 编程语言 >C++文件系统操作5 - 跨平台列出指定目录下的所有文件和文件夹

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

时间:2024-07-25 21:29:37浏览次数:16  
标签:string filepath list C++ 跨平台 文件夹 file include type

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

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);
    }

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

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__)

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__)

7. 源码地址

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


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

标签:string,filepath,list,C++,跨平台,文件夹,file,include,type
From: https://www.cnblogs.com/luoweifu/p/18324165

相关文章

  • C++ - 目录 - 一周速通
    基础入门免费教程希望可以帮助每一位编程爱好者!开源地址:点我加入开发工具随课习题文章目录C++基础-0-了解历史C++基础-1-开始入门C++基础-2-数据类型C++基础-3-变量常量C++基础-4-运算符号C++基础-5-条件判断C++基础-6......
  • 【C++】选择结构案例-三只小猪称体重
    案例问题假设有三只小猪A、B、C,在输入三者体重后希望能输出他们各自的体重并测出谁最重思路先让A与B相比较,如果A重,则让A和C相比较,如果A重则输出A最重,否则输出C最重在最开始的条件(AB相比较),如果B重,则让B与C相比较,如果B重则输出B最重,否则C最重下面是一个实例#include<iostre......
  • 【C++】选择结构- 嵌套if语句
    嵌套if语句的语法格式:if(条件1) {        if(条件1满足后判断是否满足此条件){条件2满足后执行的操作}        else{条件2不满足执行的操作}}下面是一个实例#include<iostream>usingnamespacestd;intmain4(){ /* 提示用户输入一个高考分数......
  • C++ 列式内存布局数据存储格式 Arrow
    ApacheArrow优点:   高性能数据处理:Arrow使用列式内存布局,这特别适合于数据分析和查询操作,因为它允许对数据进行高效批量处理,减少CPU缓存未命中,从而提升处理速度。   零拷贝数据共享:Arrow允许不同系统和进程之间直接共享内存中的数据而无需复制,这对于提高数据......
  • java的跨平台原理
    java的跨平台原理:Java跨平台的原理主要是通过Java虚拟机(JVM)来实现的。为啥需要跨平台:不同平台的机器码是不兼容的。在编译原理中,我们知道编译器将源代码翻译成特定平台的机器码,这样程序就可以在特定平台上运行。然而,不同平台的机器码是不兼容的,这就导致了跨平台的困难。......
  • C++设计模式汇总
    李忠建老师讲授设计模式笔记更新到抽象工厂模式:组件协作类:模板方法策略模式观察者模式单一职责类:装饰器模式桥模式模式对象创建类:工厂方法抽象工厂方法原型模式构建器模式对象性能类:单例模式享元模式接口隔离类:门面模式代理模式适配器模式中介者模式状态变......
  • C++学习笔记-operator关键字:重载与自定义操作符
    在C++编程中,operator关键字扮演着极其重要且独特的角色。它允许开发者为内置类型或自定义类型重载或定义新的操作符行为。这一特性极大地增强了C++的表达能力,使得代码更加直观、易于理解和维护。本文将深入探讨C++中operator关键字的使用,包括操作符重载和自定义操作符的基本......
  • 设计模式C++001__模板方法
    设计模式C++001__模板方法“组件协作”模式:现代软件专业分工之后的第一个结果就是“框架与应用程序的划分”,组件“协作”模式通过晚绑定,来实现框架与应用程序之间的松耦合。包括:模版方法,观察者模式,策略模式1、模板方法模式:动机:在软件构建过程中,对于一项任务,它常常有稳定的整......
  • 设计模式C++002__策略模式
    设计模式C++002__策略模式1、动机:在软件构建过程中,某些对象使用的算法是多种多样的,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。?如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?......
  • 设计模式C++003__观察者模式
    设计模式C++003__观察者模式1、动机:在软件构建过程中,我们需要为某些对象建立一种“通过依赖关系”--一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使得软件不好抵御变化。?使用面向对象技术,可以将这种依赖关系弱化,并形成......