【问题标题】:recursive folder scanning in c++C ++中的递归文件夹扫描
【发布时间】:2010-11-02 06:33:45
【问题描述】:

我想扫描目录树并列出每个目录中的所有文件和文件夹。我创建了一个从网络摄像头下载图像并将其保存在本地的程序。该程序根据下载图片的时间创建一个文件树。我现在想扫描这些文件夹并将图像上传到网络服务器,但我不确定如何扫描目录以查找图像。 如果有人可以发布一些示例代码,那将非常有帮助。

编辑:我在嵌入式 linux 系统上运行它,不想使用 boost

【问题讨论】:

    标签: c++ linux directory embedded


    【解决方案1】:

    请参阅man ftw 了解简单的“文件树遍历”。在这个例子中我也使用了fnmatch

    #include <ftw.h>
    #include <fnmatch.h>
    
    static const char *filters[] = {
        "*.jpg", "*.jpeg", "*.gif", "*.png"
    };
    
    static int callback(const char *fpath, const struct stat *sb, int typeflag) {
        /* if it's a file */
        if (typeflag == FTW_F) {
            int i;
            /* for each filter, */
            for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
                /* if the filename matches the filter, */
                if (fnmatch(filters[i], fpath, FNM_CASEFOLD) == 0) {
                    /* do something */
                    printf("found image: %s\n", fpath);
                    break;
                }
            }
        }
    
        /* tell ftw to continue */
        return 0;
    }
    
    int main() {
        ftw(".", callback, 16);
    }
    

    (甚至没有经过编译测试,但你明白了。)

    这比自己处理DIRENTs和递归遍历要简单得多。


    为了更好地控制遍历,还有fts。在此示例中,将跳过点文件(名称以“.”开头的文件和目录),除非明确将其作为起点传递给程序。

    #include <fts.h>
    #include <string.h>
    
    int main(int argc, char **argv) {
        char *dot[] = {".", 0};
        char **paths = argc > 1 ? argv + 1 : dot;
    
        FTS *tree = fts_open(paths, FTS_NOCHDIR, 0);
        if (!tree) {
            perror("fts_open");
            return 1;
        }
    
        FTSENT *node;
        while ((node = fts_read(tree))) {
            if (node->fts_level > 0 && node->fts_name[0] == '.')
                fts_set(tree, node, FTS_SKIP);
            else if (node->fts_info & FTS_F) {
                printf("got file named %s at depth %d, "
                    "accessible via %s from the current directory "
                    "or via %s from the original starting directory\n",
                    node->fts_name, node->fts_level,
                    node->fts_accpath, node->fts_path);
                /* if fts_open is not given FTS_NOCHDIR,
                 * fts may change the program's current working directory */
            }
        }
        if (errno) {
            perror("fts_read");
            return 1;
        }
    
        if (fts_close(tree)) {
            perror("fts_close");
            return 1;
        }
    
        return 0;
    }
    

    同样,它既没有经过编译测试也没有经过运行测试,但我想我会提到它。

    【讨论】:

    • FTS 样本效果很好。我必须做的唯一更改是“ftsread”->“fts_read”,我必须将 fts_read 的结果转换为 (FTSENT*)。我原以为在网上找到这样的代码会更容易,但这绝对是我找到的最干净的例子。谢谢!
    • fts_info 的值不是单个位... 选项 FTS_D 到 FTS_W 被定义为顺序值 1 到 14。如果没有注释解释,将代码作为“& FTS_F”可能会让一些人感到困惑这是怎么回事。可能有人打算在这里抢 FTS_F、FTS_INIT、FTS_NS、FTS_NSOK、FTS_SL、FTS_SLNONE 和 FTS_W……但 FTS_NS 或 FTS_NSOK 会有点奇怪。另外,我看到网络上的代码在执行“& FTS_D”,这几乎肯定不是他们想要的(获取 FTS_ERR、FTS_DEFAULT)。
    【解决方案2】:

    我是老派,没有 ftw() 适合我!这很粗糙(我已经有一段时间没有直接进行 C 编程了),而且很多东西都是硬编码的,而且我可能搞砸了 strnc*() 函数的长度计算,但你明白了。顺便说一句,K&R 中有一个类似的例子。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <sys/types.h>
    #include <dirent.h>
    
    void listdir(char* dirname, int lvl);
    
    int main(int argc, char** argv)
    {
    
      if (argc != 2) {
        fprintf(stderr, "Incorrect usage!\n");
        exit(-1);
      }
      listdir(argv[1], 0);
    
    
      return 0;
    }
    
    void listdir(char* dirname, int lvl)
    {
    
      int i;
      DIR* d_fh;
      struct dirent* entry;
      char longest_name[4096];
    
      while( (d_fh = opendir(dirname)) == NULL) {
        fprintf(stderr, "Couldn't open directory: %s\n", dirname);
        exit(-1);
      }
    
      while((entry=readdir(d_fh)) != NULL) {
    
        /* Don't descend up the tree or include the current directory */
        if(strncmp(entry->d_name, "..", 2) != 0 &&
           strncmp(entry->d_name, ".", 1) != 0) {
    
          /* If it's a directory print it's name and recurse into it */
          if (entry->d_type == DT_DIR) {
            for(i=0; i < 2*lvl; i++) {
              printf(" ");
            }
            printf("%s (d)\n", entry->d_name);
    
            /* Prepend the current directory and recurse */
            strncpy(longest_name, dirname, 4095);
            strncat(longest_name, "/", 4095);
            strncat(longest_name, entry->d_name, 4095);
            listdir(longest_name, lvl+1);
          }
          else {
    
            /* Print some leading space depending on the directory level */
            for(i=0; i < 2*lvl; i++) {
              printf(" ");
            }
            printf("%s\n", entry->d_name);
          }
        }
      }
    
      closedir(d_fh);
    
      return;
    }
    

    【讨论】:

    • strncmp(name, ".", 1) 将排除点文件。此外,最好跳过(带有警告)而不是尝试使用截断的名称进行操作。最后,不是循环,而是打印缩进这个可爱的技巧: static char spaces[] = " "; printf("%s", 空格[max(0, strlen(spaces) - count)]);
    【解决方案3】:

    您也可以使用 glob/globfree。

    【讨论】:

    • 比自己读取目录和执行匹配更容易,但仍然不是递归的。诚然,使用 GLOB_ONLYDIR 意味着您可以完全避免处理 DIRENT,但它仍然不如 ftw 方便,而且纯粹基于名称的遍历是活泼的。
    【解决方案4】:

    Boost.Filesystem 允许您这样做。查看docs

    编辑:
    如果您使用的是 Linux 并且不想使用 Boost,则必须使用 Linux 本机 C 函数。 This page 展示了许多关于如何做到这一点的示例。

    【讨论】:

      【解决方案5】:

      您将需要使用在dirent.h 中声明的目录函数。这个wikipedia page 描述了它们并包含示例代码。对于您的应用程序,一旦您确定了目录,您将需要再次递归调用处理函数来处理目录内容。

      【讨论】:

        【解决方案6】:

        我认为如果你可以使用 Qt/Embedded,有 QDir 和 QFileInfo 类 可以帮助你,虽然这取决于你是否可以使用 Qt。问题是您的系统提供了哪种 API。

        【讨论】:

          猜你喜欢
          • 2012-04-02
          • 1970-01-01
          • 2012-10-10
          • 1970-01-01
          • 1970-01-01
          • 2023-03-16
          • 1970-01-01
          • 1970-01-01
          • 2011-11-07
          相关资源
          最近更新 更多