The contents of the files are line by line and go in a "Schedule" struct. My objective is to stock these schedules in.txt files so they don't disappear after the end of execution, and to stock the structs in a vector or a list when i execute it again, by reading all files of a separated folder. I have no idea how to do this.
I supposed i could use getline() for a folder but even if it worked it would probably just give me the file names. That could work in a way, but getline() doesn't work like that.
I would use astd::filesystem::directory_iterator
.
But first things first. The first thing you could do is to define an operator>>
to read one Schedule
from an std::istream
(like a std::ifstream
).
#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;
};
... and in your container containing all the schedules you take two iterators and/or a range in order to populate it:
#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;
};
With those in place, you can create a class to open a file and provide iterators to populate your Schedules
container. It can be a rather thin wrapper around a std::ifstream
with the begin()
and end()
iterator type being std::istream_iterator<Schedule>
which can then be used to populate the Schedules
container.
#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{};
};
and you could use it to load the schedules from one file:
Schedules schedules(ScheduleFileLoader("data_directory/schedules01.txt"));
But you want to take it further by loading all files in a directory into your Schedules
container. You need to
Schedule
s are stored.ScheduleFileLoader
Schedule
s as one range. You can then encapsulate std::filesystem::directory_iterator
and a ScheduleFileLoader
in yet another class, ScheduleDirectoryLoader
. When its ScheduleFileLoader
has depleated a file, open the next file - and keep going until the std::filesystem::directory_iterator
reaches the end of the directory. This requires a custom 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;
};
With the ScheduleFileLoader
and ScheduleDirectoryLoader
in place, you can load a single file or all the files. If you need to be able to load from other sources than files, you create another adapter that provides iterators that dereferences into something that can be used to construct a Schedule
(like a std::istream_iterator<Schedule>
). It's sometimes as easy as with the single source ScheduleFileLoader
and sometimes a bit more tricky as with the ScheduleDirectoryLoader
.
Usage example:
#include <iostream>
int main() {
Schedules schedules(ScheduleDirectoryLoader("data_directory"));
for(const Schedule& s : schedules) {
std::cout << s;
}
}
The same as @Ted above.
But we can simplify a bit:
#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";
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.