我喜欢C ++中的for-loop范围,并希望像这样使用它:
#include <bits/stdc++.h>
int main()
{
for (auto s : LineReader("my-very-big-textfile.txt")) {
cout << s << endl;
}
return 0;
}
这里的目的是迭代一些数据(不先将所有数据读入容器)。在这种情况下,文本字符串是文本文件中的行。但通常它可以是任何东西(包括生成的数据)。
这里LineReader返回一个可迭代的“伪”容器。为了使其工作,for循环需要LineReader对象的迭代器。在C ++中,范围是根据开始和结束迭代器定义的。但是我想使用范围for-loop来迭代数据,其中可能在开始时不知道结尾(例如,在(过大的)文本文件中读取行而不先通过它来查找结尾。)。
所以我这样定义:
免责声明:显示原则的示例代码,因此我不会因过度使用std ::,错误处理,私有/公共关键字等而“纠缠”它...... < / p>
struct ReadLineIterator {
ifstream ifs;
string line;
ReadLineIterator() { }
ReadLineIterator(string filename) : ifs(filename) { }
bool operator!=(ReadLineIterator& other) {
return !ifs.eof();
}
ReadLineIterator& operator++() {
getline(ifs, line, '\n');
return *this;
}
string operator*() {
return line;
}
};
struct LineReader
{
string filename;
LineReader(const string& filename) : filename(filename) { }
ReadLineIterator begin()
{
return ReadLineIterator(filename);
}
ReadLineIterator end() // return a not used dummy iterator since this method must exist
{
return ReadLineIterator();
}
};
当我运行它时,它可以工作。但如果
,我会持怀疑态度bool operator!=(ReadLineIterator& other) {
return !ifs.eof();
}
是使该运算符检测序列结束的正确方法。这是因为我没有任何正确的结束对象(end()方法只返回一个虚拟迭代器),并且也没有对它进行比较。而是检查流是否为空。
但我不知道如何以其他方式做到这一点?现在我很高兴这种做法,因为它对我有用,但是知道是否有更好的方法来做同样的事情会很棒。另外很高兴知道这是否适用于所有(C ++)编译器(我正在使用GCC),如果是这样,它适用于未来的C ++标准,迭代器可能会以不同的方式处理。
答案 0 :(得分:3)
我会分两部分来做。
一个是range
类,它只是作为流迭代器的包装器:
template <class T>
class istream_range {
std::istream_iterator<T> b;
std::istream_iterator<T> e;
public:
istream_range(std::istream &is)
: b(std::istream_iterator<T>(is))
, e(std::istream_iterator<T>())
{}
std::istream_iterator<T> begin() { return b; }
std::istream_iterator<T> end() { return e; }
};
因此,这允许在基于范围的for循环中使用istream_iterator
:
for (auto const &s : istream_range<foo>(myfile))
// do something with s
istream_iterator
使用operator>>
从指定文件中提取项目,因此第二部分只是一个提取一行的微小类型:
class line {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, line &l) {
std::getline(is, l.data);
return is;
}
operator std::string() const { return data; }
};
因此,我们的for
循环变为类似:
for (auto const &s : istream_range<line>(myfile))
// do something with s
这样做的明显优势是将两者分离:我们可以使用istream_range<T>
来处理T
的文件,对于任何T
,正常流提取“正确的事情”(包括许多我们目前无法了解的自定义提取器。)
previous question的答案涵盖了一些可能性(包括LineInputIterator
似乎更接近你要求的内容。
答案 1 :(得分:1)
标准模板类std::istream_iterator<T>
充当迭代器,从istream(带operator>>(istream &, T &)
)读取连续的T对象,因此您只需要一个从istream读取行的类型T:
class line {
std::string line;
friend std::istream &operator>>(std::istream &in, line &l) {
return std::getline(in, l.line);
}
public:
operator std::string() const { return line; }
};
现在让您的LineReader
返回std::istream_iterator<line>
。