Я выполнил процедуру десериализации для объекта, используя оператор потока <<
. Сама процедура использует istreambuf_iterator<char>
для извлечения символов из потока один за другим, чтобы построить объект.Смутно об использовании `std :: istreambuf_iterator`
В конечном счете, моя цель - иметь возможность итерации по потоку с использованием istream_iterator<MyObject>
и вставки каждого объекта в vector
. Довольно стандартный, за исключением того, что у меня возникли проблемы с получением istream_iterator
до stop Итерирование, когда оно попадает в конец потока. Прямо сейчас, он просто петли навсегда, хотя звонки в istream::tellg()
указывают, что я в конце файла.
Вот код, чтобы воспроизвести проблему:
struct Foo
{
Foo() { }
Foo(char a_, char b_) : a(a_), b(b_) { }
char a;
char b;
};
// Output stream operator
std::ostream& operator << (std::ostream& os, const Foo& f)
{
os << f.a << f.b;
return os;
}
// Input stream operator
std::istream& operator >> (std::istream& is, Foo& f)
{
if (is.good())
{
std::istreambuf_iterator<char> it(is);
std::istreambuf_iterator<char> end;
if (it != end) {
f.a = *it++;
f.b = *it++;
}
}
return is;
}
int main()
{
{
std::ofstream ofs("foo.txt");
ofs << Foo('a', 'b') << Foo('c', 'd');
}
std::ifstream ifs("foo.txt");
std::istream_iterator<Foo> it(ifs);
std::istream_iterator<Foo> end;
for (; it != end; ++it) cout << *it << endl; // iterates infinitely
}
Я знаю, в этом тривиальном примере я даже не нужно istreambuf_iterator, но я просто пытаюсь упростить эту проблему, так что это более вероятно, люди будут отвечать на мои вопрос.
Таким образом, проблема заключается в том, что хотя istreambuf_iterator
достигает конца буфера потока, сам фактический поток не вводит состояние EOF
. Вызов istream::eof()
возвращает false, хотя istream::tellg()
возвращает последний байт в файле, а istreambuf_iterator<char>(ifs)
сравнивает true с istreambuf_iterator<char>()
, что означает, что я определенно в конце потока.
Я посмотрел на код библиотеки IOstreams, чтобы увидеть, как именно это определение, может ли istream_iterator
находится в конечной позиции, и в основном он проверяет istream::operator void*() const
вычисляет true
. Эта функция библиотеки IStream просто возвращает:
return this->fail() ? 0 : const_cast<basic_ios*>(this);
Другими словами, она возвращает 0
(ложь), если failbit установлен. Затем он сравнивает это значение с тем же значением в сконфигурированном по умолчанию экземпляре istream_iterator
, чтобы определить, находимся ли мы в конце.
Итак, я попытался вручную установить failbit в моей подпрограмме std::istream& operator >> (std::istream& is, Foo& f)
, когда istreambuf_iterator
сравнивает true с итератором конца. Это отлично работало и правильно завершило цикл. Но теперь я действительно смущен. Кажется, что istream_iterator
определенно проверяет на наличие std::ios::failbit
, чтобы обозначить состояние «конца потока». Но разве это не то, что для std::ios::eofbit
? Я думал, что failbit
был для условий ошибки, например, если базовый файл fstream
не мог быть открыт или что-то в этом роде.
Итак, зачем мне звонить istream::setstate(std::ios::failbit)
, чтобы получить цикл для завершения?
looping forever указывает на то, что поток испортился. Вопрос в том, почему? –
@Martin, ну, даже если я заменю поток файлов на 'std :: stringstream', возникает та же проблема. Таким образом, это не может быть проблемой низкоуровневого файла. – Channel72
Рединг @ Ответ PigBen. Причина в том, что на внешнем уровне вы используете istream_iterator (в for_each) и istreambuf_iterator внутри (operatro >>). Вы должны быть последовательными в своем использовании. ИСПОЛЬЗУЙТЕ istreambuf_iterators в обеих ситуациях, и он должен работать. –