Почему `std::istream::seekg()` влияет на поведение `std::istreambuf_iterator`?

Почему 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);. Вам наверняка хотелось in.seekg(0, std::istream::end); дойти до конца.
Eljay 28.05.2024 14:48

@Элджей Почему in.seekg(std::istream::end); == in.seekg(2, std::istream::beg);? Не могли бы вы пролить свет на этот вопрос?

John 28.05.2024 14:56

@Джон end — это enum: en.cppreference.com/w/cpp/io/ios_base/seekdir. Вероятно, в вашей системе он имеет значение 2.

NathanOliver 28.05.2024 15:00

Вы можете видеть это в выводе len:2. Ввод длиннее 2 символов.

NathanOliver 28.05.2024 15:01

Есть два seekg. Вы их смешали. Если вы хотите перейти к концу трансляции, используйте seekg(0, end). seekg(end) использует end как абсолютную позицию. end зависит от реализации, а в вашем случае, похоже, 2.

zdf 28.05.2024 15:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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() имеет две перегрузки:

  1. он может принимать в качестве аргумента одно числовое значение, которое представляет желаемую позицию относительно начала потока.
  2. он может принимать в качестве аргумента числовое смещение и некоторое значение флага, указывающее направление этого смещения. Это направление можно указать значениями 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 по наследству
Caleth 28.05.2024 16:25

Ах, да, ты прав. Я упустил этот момент. Спасибо за пояснение.

Bohdan Lakatosh 28.05.2024 16:31

@BohdanLakatosh Спасибо за подробное объяснение. Вы сказали, что когда std::istreambuf_iterator создается для данного istream, он будет указывать на текущую позицию ввода. Однако я действительно не вижу подобного утверждения в cppreference. Не могли бы вы дать мне какую-нибудь ссылку?

John 29.05.2024 04:33

@John 1 - istreambuf_iterator читает символы из входного потока; 2 - istreambuf_iteratorмагазины basic_streambufуказатель; 3 - basic_streambuf сохраняет свое состояние в виде трех указателей: начало, конец и следующий кандидат на чтение; 4 - basic_stream::seekg (вызовом pubseekpos) меняет положение «следующего указателя».

Bohdan Lakatosh 29.05.2024 10:39

Другие вопросы по теме