You have the basic idea. The std::basic_stringstream allows use of both std::basic_istream::tellg and std::basic_istream::seekg. There are a few caveats:
- if using the
.str() member funciton, a copy of the underlying string is returned in a temporary object, there is no change to the seekpos within the stringstream because you are operating on a copy;
- if using the
rdbuf() member function, it will return a pointer to the underlying string device. Outputting with the device will output the contents with and the seekpos will be advanced to the end, but eofbit is not set;
- for normal extractions with
>> reading the last item in the stringstream sets eofbit and clear() must be called on the stringstream before seeking further. If tellg() is called while the stringstream the streamstate is anything other than good, -1 is returned.
With that a short example exercising seekg() and tellg() along with clear(), where needed, should provide the explanation you are looking for. The is an amalgam of the various examples provided at the links above:
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
int main (void)
{
std::string str = "Hello, world"; /* initial string */
std::istringstream in(str); /* initialized stringstream */
std::string word1, word2; /* two string variables used below */
std::cout << "string: " << in.str() << "\n\ntellg "
<< std::setw(2) << in.tellg() << " - reading into word1\n";
in >> word1; /* read into word1 from beginning */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word1 << "\", rewinding\n";
in.seekg (0); /* seekg beginning (rewind) */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - reading into word2\n";
in >> word2; /* read into word2 from beginning */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word2 << "\", reading final word\n";
in >> word2; /* read into word2 to end of stringstream, eofbit set */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word2 << "\", eofbit set, tellg() fails, must .clear()\n";
in.clear(); /* clear required before further stringstring operations */
in.seekg (0, std::ios_base::beg); /* reposition to beginning, 2nd form */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - rdbuf() -> \""
<< in.rdbuf()
<< "\"\ntellg " << std::setw(2) << in.tellg() << " - reversing: \"";
while (in.tellg() != 0) { /* playing tell & seek back to beginning */
in.seekg (-1, std::ios_base::cur); /* seek -1 from current */
std::cout << (char)in.get(); /* output character */
in.seekg (-1, std::ios_base::cur); /* seek -1 from current */
}
std::cout << "\"\ntellg " << std::setw(2) << in.tellg() << " - all done.\n";
}
Eample Use/Output
$ ./bin/streambuf_seektellg
string: Hello, world
tellg 0 - reading into word1
tellg 6 - after reading "Hello,", rewinding
tellg 0 - reading into word2
tellg 6 - after reading "Hello,", reading final word
tellg -1 - after reading "world", eofbit set, tellg() fails, must .clear()
tellg 0 - rdbuf() -> "Hello, world"
tellg 12 - reversing "dlrow ,olleH"
tellg 0 - all done.
The example above shows how to handle manipulation of the streambuf position within the stringstring. From simple rewinds, to handling the streambuf with the read-state other than goodbit and how to use combinations of tellg() and seekg() to iterate over each of the characters in the streambuf from the end to the beginning effectively reversing the output.
Additional Example Using seekp() On std::stringstream
In response to your comment, there really isn't much difference, except in order to use the tellp() and seekp() the streambuf object must be able to have output sent to it. That means tellp() and seekp() are only relevant to std::stringstream and std::ostringstream but not std::istringstream. So in order to position the write location in the example above, you need only change the type to one of two variants that allows output. using std::stringstream is fine, e.g.
std::stringstream in(str); /* stringstrem use both seek&tell (gp) */
Now you can move the get and set the write position within it using tellp() and seekp(). For example the following gets the current read position in in by reading again into word1 and saves the position reported by in.tellg(). The position is then used by seekp() to position the for a write of "big wide world!" after "Hello, ", e.g.
std::cout << "\"\ntellg " << std::setw(2) << in.tellg()
<< " - seeking with seekp() - add \"big wide world!\"\n";
in >> word1; /* move past "Hello," */
size_t pos = in.tellg(); /* save offset from beginning */
in.seekp (pos + 1); /* seekp() past "Hello, " */
in << "big wide world!"; /* replace remaining content */
std::cout << "\nstring: " << in.str() << '\n'; /* output result */
Changes to Output
Changes to the last line of output above and then addition of the new text would result in:
...
tellg 0 seeking with seekp() - add "big wide world!"
string: Hello, big wide world!
seekg()/tellg() and seekp()/tellp() are Independent Offsets
In response to your further comments, it is important to understand that the ...g() and ...p() member functions provide access to two independent measures of offset from the beginning of the streambuf object. seekg() and tellg() are associated with the read position in the buffer. (think seek...get() and tell...get()) In contrast, seekp() and tellp() are associated with the write position in the buffer. (think seek...put() and tell...put())
An additional change to the final output of the example will make that clear. Change only the line associated with /* output results */ to:
/* output result */
std::cout << "\nstring: " << in.str() << "\ntellg : " << in.tellg()
<< "\ntellp : " << in.tellp() << '\n';
Changes to Output
The output now reveals the independent offsets reported by in.tellg() and in.tellp() after the final writes to in, e.g.
...
string: Hello, big wide world!
tellg : 6
tellp : 22
Where in.tellg() reports the position in the buffer where the next read (such as in >> word1;) will start from, while in.tellp() reports the position in the buffer where the next write will take place, (such as in << " ... of code";). After adding in << " ... of code";, outputting from the current tellg() position with std::cout << '\n' << in.rdbuf() << '\n'; would result in:
big wide world! ... of code
Look things over and let me know if you have further questions.