Understanding fstream seeking

J

Juha Nieminen

I don't really understand how seeking a std::fstream affects the
stream. Consider, for instance, this program:

//-----------------------------------------------------------------
#include <fstream>
#include <string>

int main()
{
std::fstream stream("file.txt");
if(!stream) return 1;

const std::string keyword = "hello";
const std::string replacement = "HELLO";

std::string line;
while(std::getline(stream, line))
{
size_t ind = line.find(keyword);
if(ind != line.npos)
{
stream.seekp
(size_t(stream.tellg()) - line.length() + ind - 1);
stream.write(replacement.c_str(), replacement.length());
}
}
}
//-----------------------------------------------------------------

The idea is that it would scan through a file and replace the first
appearance of "hello" in each line with "HELLO".

Problem: It makes the replacement in the first line that matches, and
then stops. I don't really understand why.

If I add "stream.seekg(stream.tellp());" after the write, then it
works as expected. However, I don't understand why I have to do that.

(Yes, I know the above code is not very portable because it assumes
that the newline character is only one byte long. Please don't nitpick
about that.)
 
J

James Kanze

I don't really understand how seeking a std::fstream
affects the stream.

It changes the write/read position (both) in the stream.
Be careful, though, seeking is only supported in very
specific cases.
Consider, for instance, this program:
//-----------------------------------------------------------------
#include <fstream>
#include <string>
int main()
{
std::fstream stream("file.txt");

Note that you are opening the stream in text mode. That
means that the only legal seeks you can make are to the
beginning, or to a position returned from a previous tell.
if(!stream) return 1;
const std::string keyword = "hello";
const std::string replacement = "HELLO";

std::string line;
while(std::getline(stream, line))
{
size_t ind = line.find(keyword);
if(ind != line.npos)
{
stream.seekp
(size_t(stream.tellg()) - line.length() + ind - 1);

This is undefined behavior on a stream opened in text mode.

In theory, I don't think it's guaranteed to even compile,
regardless of how the stream was opened. If the stream was
opened in binary mode, however, you should be able to use
something like:
stream.seekp( - (line.length() - ind) - 1, std::ios::cur ) ;
Except that you'll have to do some casting to ensure that
signed arithmetic is used.

Note too that file streams keep the get and the put pointers
in synch. A seekg changes the position of the put pointer,
and vice versa.
stream.write(replacement.c_str(), replacement.length());

You're now leaving the stream position somewhere in the
middle of the line.
}
}}

The idea is that it would scan through a file and replace
the first appearance of "hello" in each line with "HELLO".
Problem: It makes the replacement in the first line that
matches, and then stops. I don't really understand why.
If I add "stream.seekg(stream.tellp());" after the write,
then it works as expected. However, I don't understand why
I have to do that.

That seems strange. What I'd expect you to have to do is
first get the current position, using tell, then position
and write, then reseek to the previous current position.

A simpler solution (IMHO) would be to just rewrite the
entire line.
 
B

Bart van Ingen Schenau

Juha said:
I don't really understand how seeking a std::fstream affects the
stream. Consider, for instance, this program:
The idea is that it would scan through a file and replace the first
appearance of "hello" in each line with "HELLO".

Problem: It makes the replacement in the first line that matches,
and
then stops. I don't really understand why.

If I add "stream.seekg(stream.tellp());" after the write, then it
works as expected. However, I don't understand why I have to do that.

The additional seekg() call is needed, because the standard does not
allow you to arbitrarily switch between reading and writing on a stream.
Instead, you must signal to the library that a change of direction is
about to happen, by calling one of the file-positioning functions or
(when switching from writing to reading) calling flush.
These requirements have been inherited from C.

Bart v Ingen Schenau
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,782
Messages
2,569,616
Members
45,297
Latest member
EngineerD

Latest Threads

Top