在文件上实现迭代器



我想创建一个带有加载文件的类,并为给定类实现一个迭代器,以使用范围迭代器对其进行迭代

这是我的班级

class Csv
{
public:
explicit Csv(std::string filepath);
~Csv();
void load_new_file(std::string filepath) {}
private:
std::ifstream file;
};

这是我想要实现的行为

Csv csv("path");
for (auto line : csv ){
std::cout<<line<<std::endl;
}

我该怎么做?

使用std::getline的最小示例:

class End_iterator {};
class Iterator {
public:
Iterator(std::ifstream& file) : file_{file} {
next();
}
const std::string& operator*() const {
return str_;
}
Iterator& operator++() {
next();
return *this;
}
bool operator!=(End_iterator) const {
return !!file_;
}
private:
void next() {
std::getline(file_, str_);
}
std::ifstream& file_;
std::string str_;
};
class Csv {
public:
explicit Csv(std::string filepath) {
file_.open(filepath);
}
auto begin() {
return Iterator{file_};
}
auto end() {
return End_iterator{};
}
private:
std::ifstream file_;
};

C++17 允许我们在基于范围的 for 循环中使用不同类型的beginend迭代器。使End_iterator成为一种独特的类型(哨兵(更容易(也更优雅(。

这是一个满足LegacyInputIterator要求的自定义迭代器的简单示例:

class LineFileIterator
{
public:
using value_type = std::string;
using difference_type = std::ptrdiff_t;
using reference = const std::string&;
using pointer = const std::string*;
using iterator_category = std::input_iterator_tag;
LineFileIterator(): file(nullptr) {}
explicit LineFileIterator(std::ifstream& f): file(&f) {}

LineFileIterator& operator++()
{
read();
return *this;
}
LineFileIterator operator++(int)
{
LineFileIterator tmp = *this;
read();
return tmp;
}

const std::string& operator*() const
{
return currentLine;
}

friend bool operator!=(const LineFileIterator& a, const LineFileIterator& b)
{
return not a.equal(b);
}

friend bool operator==(const LineFileIterator& a, const LineFileIterator& b)
{
return a.equal(b);
}
private:
bool equal(const LineFileIterator& it) const
{
return file == it.file;
}

void read()
{
if(not std::getline(*file, currentLine))
{
file = nullptr;
}
}
std::ifstream* file;
std::string currentLine;
};

begin 方法应返回 LineFileIterator{file},end 方法应返回默认构造的 LineFileIterator。

编辑:此实现自 C++11 起有效。

在 c++ 17 中,基于 for 的范围是这样实现

{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}

你可以在这里查看

如果你想让你的类适合使用基于范围的 for 循环,你应该定义 5 个函数:

  • 开始((
  • 结束((
  • 运算符 *((
  • void 运算符 ++((
  • 布尔运算符 != (...及其他(

而且,由于您希望您的类具有迭代器,而 NOT迭代器,因此我们将简单地为底层数据结构定义一个内部迭代器,并使用此迭代器执行所有操作。

这与其他答案不同,在另一个答案中,整个班级都是一个迭代器。

这让生活变得非常轻松。

读取完整文件是单行的。

顺便说一下,解析文件也是使用std::regex_token_iterator的单行。不需要提升什么的。读取文件并将其拆分为令牌是典型的单行代码。

请参阅下面的简单程序:

#include <iostream>
#include <sstream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
using Lines = std::vector<std::string>;
std::regex re{ "," };
class Csv {
// Here we staore all lines of the file
Lines lines{};
// Local iterator to this lines
Lines::iterator lineIterator{};
// Function to read all lines of the file
void readData(const std::string& fileName);
public:
// Simple constructor
explicit Csv(std::string fileName) { readData(fileName); }
~Csv() {};
// Iterators to access the lines data
Lines::iterator begin() { lineIterator = lines.begin(); return lineIterator;  };
Lines::iterator end() { lineIterator = lines.end(); return lineIterator; };
std::string& operator *() { return *lineIterator; }
void operator ++() { ++lineIterator; }
bool operator != (const Lines::iterator& other) { return other != lineIterator;  }
};
void Csv::readData(const std::string& fileName) {
// Open File and check, if it could be opened
if (std::ifstream fileStream{ fileName }; fileStream) {
// Clear old existing data
lines.clear(); 
// Read all lines
for (std::string line{}; std::getline(fileStream, line); lines.push_back(line))
;
}
else {
std::cerr << "n*** Error: Could not open file '" << fileName << "'n";
}
}
int main() {
// Open file and read all lines
Csv csv("r:\text.txt");
// Range based for for iterating over the lines in the file
for (const std::string& line : csv) {
std::cout << "nn" << line << "   ->n";
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::ostream_iterator<std::string>(std::cout, "t"));
}
return 0;
}

最新更新