Почему std::istream::seekg() влияет на поведение std::istreambuf_iterator?
Я внимательно прочитал введение о std::istreambuf_iterator на cppreference, но прямого ответа на этот вопрос нет.
Вот фрагмент кода :
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
int main()
{
//with seekg to the end
{
std::cout << "#1 with seekg" << std::endl;
std::istringstream in{"Hello, world"};
in.seekg(std::istream::end);
std::istreambuf_iterator<char> it{in}, end;
std::string ss{it, end};
std::cout << ss << std::endl;
}
//without seekg
{
std::cout << "#2 without seekg" << std::endl;
std::istringstream in{"Hello, world"};
std::istreambuf_iterator<char> it{in}, end;
std::string ss{it, end};
std::cout << ss << std::endl;
}
//seekg to end and then to begin
{
std::cout << "#3 seekg to end and then to begin" << std::endl;
std::istringstream in{"Hello, world"};
in.seekg(std::istream::end);
std::streampos pos = in.tellg();
std::cout << "len:" << static_cast<long long>(pos) << std::endl;
in.seekg(std::istream::beg);
std::istreambuf_iterator<char> it{in}, end;
std::string ss{it, end};
std::cout << ss << std::endl;
}
}
Вот результат:
#1 with seekg
llo, world
#2 without seekg
Hello, world
#3 seekg to end and then to begin
len:2
Hello, world
Я думаю, что на выходе должно быть ничего или Hello, world в первом случае, пока на выходе llo,world.
@Элджей Почему in.seekg(std::istream::end); == in.seekg(2, std::istream::beg);? Не могли бы вы пролить свет на этот вопрос?
@Джон end — это enum: en.cppreference.com/w/cpp/io/ios_base/seekdir. Вероятно, в вашей системе он имеет значение 2.
Вы можете видеть это в выводе len:2. Ввод длиннее 2 символов.
Есть два seekg. Вы их смешали. Если вы хотите перейти к концу трансляции, используйте seekg(0, end). seekg(end) использует end как абсолютную позицию. end зависит от реализации, а в вашем случае, похоже, 2.





std::iostream::seekdir на самом деле это std::ios_base::seekdir — это значение, определяемое реализацией типа, определенного реализацией, с псевдонимом seekdir.
Он предназначен для передачи в качестве второго параметра перегрузки std::basic_istream<CharT,Traits>::seekg, которая принимает два параметра.
Вам не повезло, и seekdir можно передать и в перегрузку, принимающую 1 параметр. Я не нашел уровня предупреждения gcc, который бы это фиксировал.
Как упоминалось в комментариях, из вывода мы можем сделать вывод, что значение std::istream::end равно 2. Положение потока перемещается на 2 шага вперед. И вместо этого вы, вероятно, хотели
in.seekg(0, std::istream::end);
Переместить курсор в конец входного потока.
std::basic_istream::seekg() перемещает индикатор позиции ввода в заданную позицию.
Согласно документации позиция задается числовым значением.
std::istreambuf_iterator, когда он создается для данного istream, будет указывать на текущую позицию ввода.
Таким образом, поскольку seekg() изменяет значение входной позиции для данного istream, его вызов перед созданием итератора может оказать некоторое влияние на итератор.
Теперь давайте ответим на вопрос в конце вашего поста: почему вы не наблюдаете в своем коде ни пустую, ни полную строку?
Согласно документации seekg() имеет две перегрузки:
std::ios_base::beg, std::ios_base::end, std::ios_base::curr.Теперь в вашем коде вы передаете методу seekg() только один аргумент. Таким образом вы вызываете первую перегрузку.
Но вы передаете ему некоторое std::istream::end, которое, как я полагаю, является значением std::ios_base::end, каким-то образом импортированным в пространство имен std::istream. И вам повезло, что его значение является целым числом (или может быть неявно преобразовано в него) со значением 2.
Итак, в основном в вашем коде вы пишете in.seekg(2), что приводит к перемещению входной позиции потока на третий символ в вашей строке (символ с индексом 2). В результате печатается llo, world.
Чтобы переместить позицию ввода в конец буфера, используйте
in.seekg(0, std::ios_base::end);
Чтобы переместить позицию в начало буфера, используйте
in.seekg(0, std::ios_base::beg); или in.seekg(0);
Кстати, ваш третий пример, где вы переместили позицию ввода «в конец и обратно», сработал, потому что значение std::istream::beg равно 0. Итак, вы действительно вызвали in.seekg(0) перед созданием итератора, и он прочитал строку с ее начала.
std::istream — это класс, который определен как наследование от std::ios_base. std::istream::end естественно означает std::ios_base::end по наследству
Ах, да, ты прав. Я упустил этот момент. Спасибо за пояснение.
@BohdanLakatosh Спасибо за подробное объяснение. Вы сказали, что когда std::istreambuf_iterator создается для данного istream, он будет указывать на текущую позицию ввода. Однако я действительно не вижу подобного утверждения в cppreference. Не могли бы вы дать мне какую-нибудь ссылку?
@John 1 - istreambuf_iterator читает символы из входного потока; 2 - istreambuf_iteratorмагазины basic_streambufуказатель; 3 - basic_streambuf сохраняет свое состояние в виде трех указателей: начало, конец и следующий кандидат на чтение; 4 - basic_stream::seekg (вызовом pubseekpos) меняет положение «следующего указателя».
in.seekg(std::istream::end);==in.seekg(2, std::istream::beg);. Вам наверняка хотелосьin.seekg(0, std::istream::end);дойти до конца.