(c++)我如何读取目录的所有文件,并把它们的内容在vector/std::列表?



文件的内容逐行排列,以" Schedule"结构体。我的目标是将这些时间表储存在.txt文件中,以便它们在执行结束后不会消失,并在我再次执行时通过读取分离文件夹的所有文件将结构体储存在矢量或列表中。我不知道该怎么做。

我想我可以使用getline()文件夹,但即使它工作,它可能只是给我的文件名。这可以在某种程度上工作,但getline()不是这样工作的。

我会用std::filesystem::directory_iterator

但重要的事情先做。您可以做的第一件事是定义operator>>以从std::istream(如std::ifstream)读取一个Schedule

#include <istream>
class Schedule {
private:
friend std::istream& operator>>(std::istream& is, Schedule& s) {
// extract _one_ Schedule from the istream:
return is >> s.a >> s.b >> s.c;
}
friend std::ostream& operator<<(std::ostream& os, const Schedule& s) {
return os << s.a << ' ' << s.b << ' ' << s.c << 'n';
}
// whatever is stored in a Schedule:
int a, b, c;
};

…在包含所有调度的容器中,你需要两个迭代器和/或一个范围来填充它:

#include <ranges>
#include <vector>
class Schedules {
public:
// populating via iterators
template<class It, class EndIt>
Schedules(It begin, EndIt end) : schedules(begin, end) {}
// or via a range:
Schedules(std::ranges::input_range auto&& range) :
Schedules(std::ranges::begin(range), std::ranges::end(range)) {}
auto begin() { return schedules.begin(); }
auto end() { return schedules.end(); }
private:
std::vector<Schedule> schedules;
};
有了这些,您就可以创建一个类来打开文件,并提供迭代器来填充Schedules容器。它可以是std::ifstream的一个相当薄的包装,begin()end()迭代器类型是std::istream_iterator<Schedule>,然后可以用来填充Schedules容器。
#include <fstream>
#include <iterator>
class ScheduleFileLoader {
public:
ScheduleFileLoader() = default;
ScheduleFileLoader(const std::filesystem::path& path) : ifs(path) {}
ScheduleFileLoader(const std::filesystem::directory_entry& dent) :
ScheduleFileLoader(dent.path()) {}
operator bool() const { return ifs.good(); }
void open(const std::filesystem::path& path) {
if(ifs.is_open()) ifs.close();
ifs.open(path);
}
void open(const std::filesystem::directory_entry& dent) {
open(dent.path());
}
using iterator = std::istream_iterator<Schedule>;
iterator begin() { return iterator{ifs}; }
iterator end() { return iterator{}; }
private:
std::ifstream ifs{};
};

,您可以使用它从一个文件加载调度:

Schedules schedules(ScheduleFileLoader("data_directory/schedules01.txt"));

但是您想通过将目录中的所有文件加载到您的Schedules容器中来更进一步。你需要

  • 遍历包含Schedules的文件所在目录中的所有文件。
  • 使用ScheduleFileLoader
  • 打开每个文件
  • 提供迭代器来处理Schedules流作为一个范围。

然后可以将std::filesystem::directory_iteratorScheduleFileLoader封装在另一个类ScheduleDirectoryLoader中。当ScheduleFileLoader耗尽一个文件时,打开下一个文件—并继续操作,直到std::filesystem::directory_iterator到达目录的末尾。这需要自定义迭代器。

#include <filesystem>
class ScheduleDirectoryLoader {
public:
ScheduleDirectoryLoader(const std::filesystem::path& dir) : m_dir(dir) {}
ScheduleDirectoryLoader(const std::filesystem::directory_entry& dent) :
ScheduleDirectoryLoader(dent.path()) {}
struct iterator {                         // the custom iterator
using difference_type = std::intptr_t;
using value_type = Schedule;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::input_iterator_tag;
iterator() = default; // end iterator
iterator(const iterator& other) : dit(other.dit), fit(other.fit) {}
iterator(iterator&&) = default;
iterator& operator=(const iterator& other) {
dit = other.dit;
// the ScheduleFileLoader is not copyable because ifstream is not
fit = other.fit;
return *this;
}
iterator& operator=(iterator&&) = default;
iterator(const std::filesystem::path& dir) : dit(dir) {
if(dit != std::filesystem::directory_iterator{}) {
sfl.open(*dit);
if(sfl) fit = sfl.begin();
}
}
const Schedule& operator*() const { return *fit; }
// The advancement of this iterator:
// 1. step the ScheduleFileLoader::iterator
// 2. if it reaches the end of the file, ...
// 3. step the directory iterator
// 4. if it doesn't reach the end of the directory, ...
// 5. open the new file
// 6. if opening the file succeeds, get a new begin() iterator
iterator& operator++() {
if(++fit == ScheduleFileLoader::iterator{}) { // end of this file
if(++dit != std::filesystem::directory_iterator{}) {
// not last in directory
sfl.open(*dit);
if(sfl) fit = sfl.begin();
}
}
return *this;
}
iterator operator++(int) {
iterator copy(*this);
++(*this);
return copy;
}
bool operator==(const iterator& rhs) const { return fit == rhs.fit; }
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
private:
std::filesystem::directory_iterator dit{};
ScheduleFileLoader sfl;
ScheduleFileLoader::iterator fit;
};
iterator begin() { return m_dir; }
iterator end() { return {}; }
private:
std::filesystem::path m_dir;
};
有了ScheduleFileLoaderScheduleDirectoryLoader,您可以加载单个文件或所有文件。如果您需要能够从文件以外的其他来源加载,您可以创建另一个适配器,该适配器提供迭代器,该迭代器可以解引用到可用于构造Schedule(如std::istream_iterator<Schedule>)的内容。有时像使用单源ScheduleFileLoader一样简单,有时像使用ScheduleDirectoryLoader一样棘手。

使用例子:

#include <iostream>
int main() {
Schedules schedules(ScheduleDirectoryLoader("data_directory"));
for(const Schedule& s : schedules) {
std::cout << s;
}
}

和上面的@Ted一样。

但是我们可以简化一下:

#include <iostream>
#include <filesystem>
#include <vector>
int main()
{
namespace fs = std::filesystem;
std::vector<fs::directory_entry> dirents{fs::directory_iterator("."), fs::directory_iterator{}};
for (auto const& dir: dirents) {
std::cout << dir.path() << "n";
}
}

相关内容

最新更新