How to replace words in a text file with seekp

My actual problem is much larger but figuring out how to do this example will help me. I want to overwrite data that already exists in a text file but I can't seem to do this.

ex.
[file.txt]
I have five fingers.

My current code:
ofstream File;
File.open("file.txt", ios::out);
File.seekp(8, ios::beg);
File << "four";
File.close();

I have tried different combinations of ios on the second line, but it either
a) erases the whole file and writes "four" at the begining or end
b) writes "four" at the very end ("I have five fingers.four")
c) erases the whole file and writes "four" eight characters into the file

I know I can get it to work by reading in the file and going through it word by word, then re-outputting the right words and replace then at the correct time, but the actual file is extremely large so processing the whole file is not an option.
Assuming I know the starting byte locations of everything I want to change, byte 8 in this case, how can I get the file.txt to display this:

I have four fingers

Is there any way to do this and avoid using ios::binary? I've tried changing File << "four"; to File.write("four", 8); but it just writes "four" eight characters into the file and nothing else is there. Thanks
You can read the entire file, modify what you get and output that overwriting the entire file
The ultimate goal is to be able to replace something given its memory location, without processing or storing the whole file in ram.
Try to open the file both for input and output ( you will need a fstream for that )
Well, I got it to work with fstream. Whats weird is if that I have File.open("file.txt", ios::out) it will replace the whole file, but if I have File.open("file.txt", ios::out | ios::in) or only ios::in it WILL output (I supposed because of the fstream) and will "type over" the text currently in the file, exactly what I wanted.

edit:
I actually got it to work with ofstream too. It seems very weird, but it works
ofstream File;
File.open("file.txt", ios::in); //ios::in with ofstream seems counterintuitive but works.. anyone know why?
File.seekp(8, ios::beg);
File << "four";
File.close();
With ios::in the stream will preserve the old data to allow input operations.
Maybe ofstream with ios::in allows input but doesn't provide functions to do so.
That's non-standard. You're lucky the people who wrote your compiler's STL let that work. I wouldn't have.

Why not just use the proper stream type?
You could just read the file in chunks and use a temp file to store the new data. Read the first 40-50 characters into memory, make the changes needed, place the data into the temp file. Then go onto the next 50 and repeat the process until the end of file is reached, then copy everything from the temp file into the normal file and delete the temp file.

EDIT: Oh, and you should make sure to include ios::app or ios::trunc depending on if you want to just open or create your file, don't rely on your compiler to do it for you or for the defaults to work correctly.
Last edited on
From what I can tell your problem is the way you are opening the file.
ios::out creates a new file or deletes the old one and creates a new one. Your code could work if you add ios::app("app" is short for "append") like so:
1
2
3
4
5
ofstream File;
File.open("file.txt", ios::out | ios::app);
File.seekp(8, ios::beg);
File << "four";
File.close();


This way the old file is kept, you open it, the pointer is automatically set at the end-of-file, you move it and do whatever.

Later edit: If you need to both write and read at the same time your code should be like this:
1
2
3
4
5
fstream File;
File.open("file.txt", ios::out | ios::app | ios::in);
File.seekp(8, ios::beg);
File << "four";
File.close();
Last edited on
The ios::app means append. There is no need to do that.

1
2
fstream File( "file.txt", ios::in | ios::out );
// File is now open for reading and writing  

I also recommend you open with the ios::binary flag set, as the file positioning functions don't work properly otherwise.

Here's an example program that plays with the file given as argument. Try running it on its own source code. :-)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <cctype>
#include <fstream>
#include <iostream>
using namespace std;

int main( int argc, char** argv )
  {
  if (argc != 2)
    {
    cout << "usage:\n  "
         <<    argv[ 0 ] << " FILENAME\n\n"

            "Toggle the character case of all the alphabetic letters in FILENAME.\n";
    return 1;
    }

  fstream   f( argv[ 1 ], ios::in | ios::out | ios::binary );
  streampos pos = 0;

  while (true)
    {
    int c = f.get();

    if (c == EOF) break;

    pos = (unsigned)pos + 1;

    if (isalpha( c ))
      {
      c = isupper( c ) ? tolower( c ) : toupper( c );

      f.seekp( (unsigned)pos - 1, ios::beg );
      f.put( c );
      f.seekg( pos, ios::beg );
      }
    }

  return 0;
  }

Hope this helps.
Topic archived. No new replies allowed.