我想用加载的文件创建一个类,并为给定的类实现一个迭代器,以便用范围迭代器对它进行迭代
这是我的课
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循环中使用不同类型的begin
和end
迭代器。 使end_iterator
成为不同类型(sentinel)更容易(也更优雅)。
这是一个满足LegacInPutIterator需求的自定义迭代器的简单示例:
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个函数:
因为您希望您的类有一个迭代器,而不是一个迭代器,所以我们将简单地为底层数据结构定义一个内部迭代器,并用这个迭代器执行所有操作。
这与其他答案不同,在其他答案中,整个类都是一个迭代器。
这让生活变得很轻松。
读取完整的文件是一个单行程序。
顺便说一下,通过使用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 << "\n\n" << line << " ->\n";
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::ostream_iterator<std::string>(std::cout, "\t"));
}
return 0;
}