1

I'm trying to write a function that when called, will search my text file for a match to the parameter, then "update" it by overwriting the file.

here is my current function:

EDIT: Here is the new updated code, still not working :/

Here is the line it sends to UpdateGem: (Example sTempTxt: "AB:5.55")

ostringstream stream; 
stream << std::setprecision(3)  << fEindgem;
string sTempTxt = sVak + ":" + stream.str() + "\n";
UpdateGem(sTempTxt);

void UpdateGem(string text)
{
     ifstream f;
     f.open("GEM.txt");
     string sGEMS[100];
     string temp[3];
     splitstring(text, ":", temp[0], temp[1]);
     bool OverWrite;
     int count = 0;
     string Delete, line;

     while(true)
     {
         getline(f, sGEMS[count], '\n');
         if(f.eof()) break;
         splitstring(sGEMS[count], ":", temp[1], temp[2]);
         if (temp[0] == temp[1])
         {
              OverWrite = 1;
              Delete = sGEMS[count];
         }
         count++;
     }

// Don't set count to 0, since we need it
// count = 0;
   ofstream f2;
   f2.open("GEM2.txt"/*,std::ios_base::app*/);
   if (OverWrite) 
   {
      f.seekg(0, std::ios::beg);
      for (int i = 0; i < count; ++i) 
      {
         if (sGEMS[i] != Delete) f2 << sGEMS[i];
      }
   }
     f.close();
     f2.close();
     remove("GEM.txt");
     rename("GEM2.txt", "GEM.txt");

     ofstream file;
     file.open("GEM.txt",std::ios_base::app);
     file << text;
     file.close();     


}

The line it has to replace takes the form of NAME:NUMBER, in which the number can be different, so I am using a splitstring function to compare the name to the name of the line found, then completely erasing that line and "updating" it by re-adding it later. However, my current code only writes the updated line to the file, not the old ones...

user1462344
  • 11
  • 1
  • 4
  • 4
    Overwriting in the middle of a file is a fraught process if the replacement text is not exactly the same size as the text being replaced. – Jonathan Leffler Jun 17 '12 at 21:03
  • You have two while loops that do `getline` on f. If you reach eof in the first loop, the second one won't read any lines. You should reset stream's internal get pointer somewhere between the loops. `f.seekg(0, std::ios::beg);` – jrok Jun 17 '12 at 21:07
  • Could you please explain that in laymens terms, I'm a beginner still.. – user1462344 Jun 18 '12 at 00:11
  • There is code in the answers to [Write in the middle of a binary file without overwriting any existing content](http://stackoverflow.com/questions/10467711/) that might give you some idea about inserting in the middle of a file. That question is tagged C, so the answer is C too, but it may help you get some ideas about inserting information into the middle of a file. Shortening a file is simpler, but also requires care. Overwriting a section of the file with the same amount of new data is trivial by comparison. That's why fixed-length record data files are popular; they're easy, too. – Jonathan Leffler Jun 18 '12 at 13:36

1 Answers1

0

It looks like from your program, you are assuming there is never more than 100 lines of text in "GEM.txt". However, in your initial scan of the file, you do not remember how many lines were read in. So, to make up for that, you try to remember how many lines by re-reading in the original "GEM.txt" file, and writing out your saved lines into "GEM2.txt".

As jrok correctly points out, in your second loop, you are reading from "GEM.txt" starting from when you already encountered feof in your first loop. So, your second loop never gets inside the while loop, since the getline call thinks there is nothing to get (since f is at the end of file). So jrok gave you the line of code to add just before your second while loop:

f.seekg(0, std::ios::beg);

However, since you have all the lines saved in an array already, you can just write out the sGEMS array into your new file directly.

// Don't set count to 0, since we need it
// count = 0;
ofstream f2;
f2.open("GEM2.txt"/*,std::ios_base::app*/);
if (Overwrite) {
    for (int i = 0; i < count; ++i) {
        if (sGEMS[i] != Delete) f2 << sGEMS[i] << std::endl;
    }
}
//...

Instead of using an array, it would be safer to use a vector, in case the file is longer than 100 lines. And, you won't need count anymore, since it is tracked by the size of the vector.

std::vector<std::string> sGEMS;
//...
while (true) {
    getline(f, line);
    if (f.eof()) break;
    sGEMS.push_back(line);
    //...
}

If you expect the "GEM.txt" file to be really large, you may have to rethink how you are processing the file. In particular, you should only remember which line number to skip, and copy lines from "GEM.txt" to "GEM2.txt" as you were originally trying to do.

Finally, in the final write in your code, you open the file for append, but not for output. C++ takes this to mean that you want to throw away the contents of the file. Instead, add the ios_base::out flag to your open call.

ios_base::openmode mode = ios_base::out|ios_base::app;
file.open("GEM.txt",mode);
file << text << std::endl;
file.close();

But, instead of doing that, you could have just tacked it onto the end of f2 before you closed it.

With my suggestions, the final form of UpdateGem should be:

void UpdateGem(string text) {
    ifstream f;
    ofstream f2;
    vector<string> sGEMS;
    string temp[3];
    bool OverWrite;
    string Delete, line;

    splitstring(text, ":", temp[0], temp[1]);
    sGEMS.reserve(100);
    f.open("GEM.txt");
    while (true) {
        getline(f, line);
        if (f.eof()) break;
        sGEMS.push_back(line);
        splitstring(line, ":", temp[1], temp[2]);
        if (temp[0] == temp[1]) {
            OverWrite = 1;
            Delete = line;
        }
    }
    f.close();
    f2.open("GEM2.txt"/*,std::ios_base::app*/);
    if (OverWrite) {
        for (int i = 0; i < sGEMS.size(); ++i) {
            if (sGEMS[i] != Delete) f2 << sGEMS[i] << std::endl;
        }
    }
    f2 << text << std::endl;
    f2.close();
    remove("GEM.txt");
    rename("GEM2.txt", "GEM.txt");
}

I tested the code with the following main program:

int main () { UpdateGem("Test:0001"); }

And implemented splitstring like this:

bool splitstring (string s, string match, string &a, string &b)
{
    int x = s.find(match);
    if (x != string::npos) {
        a = s.substr(0, x);
        b = s.substr(x+match.size());
        return true;
    }
    return false;
}

When fed the following input "GEM.txt":

a:x
b:x
Test:1234
c:x

The program modifies the contents of "GEM.txt" into:

a:x
b:x
c:x
Test:0001
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thank you for your help! I will try this later when I get home. Also, it is safe to assume the file will never contain more then 100 lines because there is only an X amount of types there can be, and this will never be greater than 100. I also have no idea what vectors are. I'm learning myself to code by using my experience from PAWN and the occasional googling :) – user1462344 Jun 18 '12 at 13:04
  • Okay I have tried this out, but instead of adding new lines to it the old data gets completely erased and only the new line is added. – user1462344 Jun 18 '12 at 13:31
  • I already accepted it because your answer was by far the best. Trying the simple change now. EDIT: just tried removing f.seekg(0, std::ios::beg); and using your loop, but it still isnt working. – user1462344 Jun 18 '12 at 17:12
  • It didn't work, now I'm trying yours, which isn't working either :/ – user1462344 Jun 18 '12 at 18:44
  • No didn't think that, however my code still isnt working. Could you please write it out in full because I think I'm doing something wrong again. I'll update the code in the main post. Thanks for your help again! :) – user1462344 Jun 19 '12 at 12:16
  • @user1462344: Can you explain in more detail? I tested the code, so I know it works for the way I used it. – jxh Jun 19 '12 at 19:42
  • I put all my code into a pastebin. If you could update the code so it works and point out what was wrong, I'd be eternally grateful :) http://pastebin.com/1cfyNqnz – user1462344 Jun 20 '12 at 12:21
  • @user1462344: I think you have to tell me what isn't working first. Are you saying you are still getting only the final line? – jxh Jun 20 '12 at 14:47
  • Yes, in short, all lines get deleted, and only the new line gets added – user1462344 Jun 20 '12 at 17:52
  • @user1462344: Please test the new `UpdateGem` routine in a small program by itself, similar to how I described my test, ad see if the routine is the problem. My test shows it is not. If you verify the routine is not the problem anymore, then this issue should be considered resolved, and you have a different problem. – jxh Jun 20 '12 at 18:57