我不得不承认,我以前对scandir 或任何相关的 C 函数一无所知。但根据我通过阅读<dirent.h> 上的文档了解到的情况,它是一个辅助函数,用于包装对较低级别 APIS 的多次调用。
在这种情况下,我更喜欢创建一个实现相同功能的 C++ 包装器,并使用类似 C++ 的 API。
从 DIR 类型的包装器开始,以确保它被正确清理:
namespace cpp {
struct DIR {
::DIR * dir;
DIR(const std::string & path) : dir(opendir(path.c_str()))
{
if(dir==0) throw std::runtime_error("Unable to open path");
}
~DIR() { if(dir!=0) closedir(dir); }
operator ::DIR * () { return dir; }
};
}
scandir 函数现在可以这样实现:
template<class Filter, class Compare>
std::vector<dirent> scandir(const std::string & path, const Filter& filter, const Compare& compare) {
cpp::DIR dir(path);
std::vector<dirent> res;
dirent entry, *entry_ptr = &entry;
while( ::readdir_r(dir, &entry, &entry_ptr)==0 ) {
if( entry_ptr==0 ) break;
if( filter(entry) ) res.push_back(entry);
}
std::sort(begin(res), end(res), compare);
return res;
}
然后这样称呼它:
std::string path = ...;
...
auto filter = [] (const dirent& entry) { return entry.d_name[0]!='.'; };
auto compare = [](const dirent & lhs, const dirent & rhs) {
return std::strcmp(lhs.d_name, rhs.d_name)<0;
};
auto entries = cpp::scandir(path, filter, compare);
readdir_r() 的使用使得上面的实现是线程安全的,尽管应该添加更多的检查来报告它返回的任何错误。
注意:上面的代码需要包含以下标头:
#include <dirent.h>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>