在目录

时间:2019-10-04 07:13:19

标签: c++

我处于一种反复需要知道目录结构中给定目录中有多少个文件的情况。但是,由于设备上的内存限制,我无法在所有目录中保留文件数量的内存索引。 根据STL文档,我可以选择以下几种方式:

#include <filesystem>
#include <iostream>

int main(void)
{
    std::string path = ".";

    size_t count = 0;
    for (const auto& entry : std::filesystem::directory_iterator(path))
    {
        ++count;
    }
    std::cout << "Number of files in dir \".\" is: " << count << std::endl;
    return 0;
}

尽管它是相当有效的代码,但我想知道是否没有更快的方法,甚至可以消除对foreach的使用?

如果这有帮助,我正在使用C ++ 17,并且代码将在macOS,FreeBSD,OpenBSD,Linux,NetBSD上运行。内存限制适用于运行Linux和NetBSD的设备。在其中一些设备上,我的内存总计不足16kB。

2 个答案:

答案 0 :(得分:1)

似乎没有简单的方法。.虽然文档说为directory_iterator定义了开始和结束,但是它的行为不像基于范围的。

directory_iterator begin( directory_iterator iter ) noexcept;
(1)   (since C++17)

directory_iterator end( const directory_iterator& ) noexcept;
(2)   (since C++17)
     

1)返回iter不变

     

2)返回默认构造的   directory_iterator,作为结束迭代器。参数是   忽略了。这些非成员函数允许使用   directory_iterators和基于范围的for循环。

#include <fstream>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    fs::directory_iterator a(".");

    for(auto& p: a)
        std::cout << p.path() << '\n';

    //std::cout << (std::end(a) - std::begin(a)) << '\n'; // apparently impossible,
    std::cout << 
              std::distance(a, 
                            fs::directory_iterator()) << '\n'; // always returns 1
    std::cout << 
              std::distance(fs::directory_iterator("."), 
                          fs::directory_iterator{}) << '\n'; // need a new iterator
}

每当您增加directory_iterator时,除非到达列表的末尾,否则它将不等于默认构造的默认值。增量运算符是“魔术”,它扫描文件系统以查找下一个条目。使用标准库执行此操作的方法是:

fs::directory_iterator a(".");
int count = 0;
for(auto p: a) {
    ++count;
}
std::cout << count << '\n';

这正是std::distance所做的。迭代器的状态在增量过程中发生变化,必须予以考虑。

很明显,目录的内容在循环执行过程中会发生变化,因此实际的循环看起来可能会复杂得多。

答案 1 :(得分:0)

以下内容直接使用POSIX API,它将与不使用文件系统的方法(C ++ 17)一样快且具有内存效率。算是“。”和“ ..”,您可能要对此进行补偿。

#include <dirent.h>
#include <optional>

std::optional<int> dir_entries(const char* path)
{
  DIR* dp{::opendir(path)};
  if (!dp)
  {
    // Alternatively, return -1 instead of an optional, throw an exception, etc.   
    // Jury it still out on this one :)
    return std::nullopt;
  }

  int entries{0};
  while (::readdir(dp.get()))
    ++entries;

  ::closedir(dp);
  return entries;
}