Hello again! In this video, we are going to look at random access to streams.
Normally when we access a stream, we do it sequentially. So we start at the beginning and go through
it step by step until we get to the end.
And then we stop.
But it is possible to access streams randomly, which means we can jump to any position we want.
And read or write data from that position.
The stream classes in C++ have a data member, which keeps track of where the last operation stopped.
That is known as the position marker. If you have performed a write operation,
this will be at the end of the stream.
So just after the data that you've written to the stream.
If you did a read operation, this will be pointing to the following data.
So that is the start of the data that will be retrieved by the next read operation.
And normally that is controlled by the stream.
There are some cases where the programmer can set the position of this marker. If you have a file stream
and it was not opened in append mode, or if you have a string stream. In that case, you can set the position
of the marker. And you can use that to read or write data, anywhere in the stream, because that controls
where the next operation will take place.
The streams have member functions. The seek member functions will change the position of the marker.
They have different names for different streams.
This is because they come from C, which doesn't have the notion of overloaded functions or indeed
functions.
If you have an input stream, then seekg() will set the current position.
So it's 'g' for a "get" operation.
If you have an output stream, then seekp() will set the current position, so that's for a "put" operation.
And there are also member functions which will give you the current position of the marker. tellg()
will give you the position for an input stream and tellp() will give its for an output stream.
These operations are only defined where they make sense, so seekg() and tellg() are only defined for input
streams and seekp() and tellp() are are only defined for output streams.
They are defined for iostreams, but they do not do anything useful.
They give you a runtime error and they put the stream into an invalid state.
So do not do that!
If you have an fstream and it is opened in append mode, then seekp() does nothing. The stream will always write
the output at the end of the file.
The return value from tell is an object of type "pos_type", and this represents a
position in the stream.
It can be converted to an int, so you can print it out or compare it against an int. If the stream is
in an invalid state when you call the tell operation,
this operation will fail, and you can find that by comparing the return value to minus one.
So if you do a tell and the return value is not minus one, then the operation succeeded ,and you have
a valid position marker.
You can pass this result to a seek operation, so you can seek to a particular position.
And that is an absolute position.
You can also seek to a relative position.
So, so many bytes from the beginning or end of the stream, or so many bytes from the current position.
So for example, you can say that's the base position is the end, ios_base::end, and you want to move
minus 10 bytes from that.
So that means 10 bytes before the end of the stream.
One thing you can do is to save the current position of the marker, so you can get it later.
So that is a bit like putting a bookmark in your file or your stream. So you could open some output stream
and send some data to it, and then you save the position of the marker.
So that is where the stream currently is.
And then you can write some more data to the stream. And if you later decide that you want to go back and
undo that output, or overwrite it, then you can seek to the position.
So this is the bookmark and this will return to the bookmark.
And then if you write again, this will overwrite all the data that you put here.
So here is an example.
We open an output, an ostringstream.
We have some data we are going to send to it. And then we call tellp() to get the position marker for
the stringstream.
Then we compare the position of the marker and the number of characters we have written to the stream.
And then with the stream at that position, we then write "hello".
So that is going to make the marker move forward five bytes; five bytes for "hello".
And then we can check the new value of the marker.
Then we print out all the data in the string. And then we go back to the position marker that we saved
earlier.
So that is before
"hello".
And then we put "goodnight" on the stream.
So this is going to overwrite "hello" with "goodnight".
So let's see what happens.
So we wrote 18 characters to the stream, so "It is time to say" has 18 characters.
The stream marker is 18 bytes into the stream. So 'I' goes at byte 0, 't' goes at byte one and so on, and the
last character went at byte 17.
So the next operation will be at byte number 18.
Then we send another five bytes to the stream, so the marker moves ahead five bytes.
It is now at position number 23, and we have a string which says "It is time to say hello".
Then we go back to the position before we wrote "hello", and we write, "goodnight", so we have
overwritten
"hello" with "goodnight".
So you could use this with a file stream to modify a file in place.
However, the best way to modify a file is actually to read it into memory.
Make your changes in memory and then write them over the original file. So you could, for example,
read the data into an istringstream.
Then you get the bound string, and make the changes to the data, in the string, and then you write that
string over the original file.
If you use seek and tell, this will modify the file in place.
It is more complicated, it is harder to get right and if you do make a mistake, then you have overwritten
the data, so the original file is no longer available. Unless you made a back up.
This could be useful, though, if you have a very large file, or a very limited amount of memory, and
you cannot read the entire contents of the file into memory in one go.
Okay, so that is it for this video, it is time for me to say goodnight. Or rather, good afternoon!
I will see you next time.
Meanwhile, keep coding!