Read from a text file and input string somewhere in between [FINISHED - THX]

Hello, I am trying to read a text file using 'fstream' and writing to it at the same time.

- My text file contains the names of all the 12 months in the 3 letter format i.e. Jan, Feb, etc. and there is one empty line after each month
- Under these months I am storing a person's information, i.e. Last name and first name (in same line) and date in the next line and then another empty line
- My function reads this file and its purpose is to input a new name with the date but in alphabetical order under a specific month.

** I have figured out how to detect what month I am going to store under, but I am having trouble when it comes to writing the new name and date, right now it finds the month that it has to go under and writes the record of name and date, but erases the other stuff under (i.e the names of other months that were written before)**

??Therefore how do I read a next line without using getline and then write between the current line and the next line WITHOUT losing/erasing the next lines' data??

Here is what I have so far:

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
40
41
42
while (!stored) {
      if ((PersonInfo.eof()) && (!stored)){
         cout << "Errror: Reached end of file, did not store person's record!" << endl;
         return(false);
      }
      
      getline(PersonInfo, current_line);
      
      if (current_line == month){
         cout << "Found month" << endl;
         cout << month << endl;
         //PersonInfo.flush();
         getline(PersonInfo, current_line);
         cout << current_line << endl;
         getline(PersonInfo, current_line);
         cout << current_line << endl;
         if (current_line == next_month) {
            PersonInfo.seekp(0, ios::cur);
            cout << "Storing record..." << endl;
            cout << l_name << ", " << f_name << endl;
            cout << day << " " << month << " " << year << endl;
            
            PersonInfo << l_name.data() << ", ";      
            PersonInfo << f_name << endl;
            PersonInfo << day << " " << month << " " << year << endl << endl;
            if (!PersonInfo.good()) {cout << "An error occured while trying to write to file!" << endl;}
            PersonInfo.close();
            if (!PersonInfo.good()) {
               cout << "An error occured closing file!" << endl;
               return(false);
            }
            return(true);
         }
         else {cout << current_line << endl;}
         /*while (!found_place) {
            pin1 = current_line.find_first_of(",", 0);
            getline(PersonInfo, current_line);
         }*/
      }
      else {stored = false;}
      
   }  



thankz in advance =)
Last edited on
There are really only two ways to do it.

method one
Use a std::deque and read the whole file into memory, process it, then write it back out.
1
2
3
4
5
6
7
8
#include <deque>
#include <fstream>
#include <string>
...

deque <string> file_lines;
while (getline( PersonInfo, current_line ))
  file_lines.push_back( current_line );

Now you can insert, delete, reorder, etc your lines. Once done, write the deque's contents back to file.
1
2
3
4
5
6
PersonInfo.seekp( 0 );
for (deque <string> ::iterator
     p_current_line  = file_lines.begin();
     p_current_line != file_lines.end();
     p_current_line++)
  PersonInfo << *p_current_line << '\n';

You could use a std::vector instead of the std::deque if you wanted. But the deque is better suited to modifying the lines (inserts, deletes, etc) than a vector --particularly if the file is large.

method two
Create a temporary file. It would go something like this:
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
#include <cstdio>
#include <fstream>
#include <string>
...

// Create a temporary filename and open it for writing
string temp_filename = tmpnam( NULL );
ofstream temp_PersonInfo( temp_filename.c_str() );
if (!temp_PersonInfo) ...

// For each line in the source file
while (getline( PersonInfo, current_line ))
  {
  // write it to the temp file
  temp_PersonInfo << current_line;

  // and maybe insert some lines after it
  if (current_line == month)
    {
    ...
    }
  }

// Close both files. This next stuff is important!
PersonInfo.close();
temp_PersonInfo.close();

if (remove( original_filename.c_str() ) != 0)
  {
  cerr << "I could not overwrite " << original_filename << " !\n";
  return false;
  }
if (rename( temp_filename.c_str(), original_filename.c_str() ) != 0)
  {
  ...
  }

You could just rename the original filename to something else if you wish to back it up instead of removing it. Take note that the files must be closed by all processes (though your program should be the only one messing with them) in order for the renaming and removing to work. They may fail for other reasons.

Hope this helps.
Last edited on
I have tried to implement the std::deque way so far... but it still is not adding the string called whole_name into the memory.. am I doing it incorrectly?

here is my 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
40
41
42
43
44
45
46
// Stores all the information at the end of the test
bool Gather_Info::store_info(string &l_name, string &f_name, string &service_n, int day, string &month, int year) {
   string current_line, next_month, whole_name;
   fstream PersonInfo("Person_Info.txt", ios::in | ios::out);
   deque <string> file_lines;
   
   if (!PersonInfo) {
      cout << "Error: Unable to access file for writing!" << endl;
      return(false);
   }
   
   bool stored = false, found_place = false;
   whole_name = l_name + ", " + f_name;
   month = month.substr(0,3);
   
   if (month.compare("Jan") == 0) {next_month = "Feb";}   
   if (month.compare("Feb") == 0) {next_month = "Mar";}  
   if (month.compare("Mar") == 0) {next_month = "Apr";}
   if (month.compare("Apr") == 0) {next_month = "May";}
   if (month.compare("May") == 0) {next_month = "Jun";}
   if (month.compare("Jun") == 0) {next_month = "Jul";}
   if (month.compare("Jul") == 0) {next_month = "Aug";}
   if (month.compare("Aug") == 0) {next_month = "Sep";}
   if (month.compare("Sep") == 0) {next_month = "Oct";}
   if (month.compare("Oct") == 0) {next_month = "Nov";}
   if (month.compare("Nov") == 0) {next_month = "Dec";}
   if (month.compare("Dec") == 0) {next_month = "Jan";}
   
   while (getline(PersonInfo, current_line )) {
      if (current_line == month){
         getline(PersonInfo, current_line);
         file_lines.push_back(current_line);
         getline(PersonInfo, current_line);
         if (current_line == next_month) {
            file_lines.push_back(whole_name);
         }
      }
      file_lines.push_back(current_line);
   }
   
   PersonInfo.seekp(0);
   for (deque <string> ::iterator p_current_line  = file_lines.begin(); p_current_line != file_lines.end(); p_current_line++) {
   PersonInfo << *p_current_line << '\n'; }
   return(true);

}
Last edited on
I think your conditions have a hole in them inside your while loop.

It appears that you always want to save the line read from file, so you might as well just push it as soon as you get it --before messing with any conditionals.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while (getline(PersonInfo, current_line)) {
  // save the line (no matter what it is)
  file_lines.push_back(current_line);

  // if the line is the month we are looking to update
  if (current_line == month) {
    // copy all existing data
    while (getline(PersonInfo, current_line)
    and    (current_line != next_month))
      file_lines.push_back(current_line);
    // insert our stuff (before the next month begins)
    file_lines.push_back(whole_name);
    // don't forget the next month's line, if there was one
    if (PersonInfo) file_lines.push_back(current_line);
  }
}

Hope this helps.
Last edited on
For some odd reason my 'Person_Info.txt' file is not 'good' =S

Below is your code but with checks through the whole algorithm to see where the file is not good... and the only out put i get is:
Error: Unable to complete reading from the record file!
Error: Unable to close the record file!


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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int i = 0;

   while (getline(PersonInfo, current_line)) {
      i++;
      // save the line (no matter what it is)
      file_lines.push_back(current_line);
      if(!PersonInfo.good()) {
         cerr << i << ".1. Error: Unable to read from the record file!" << endl;
      }
      // if the line is the month we are looking to update
      if (current_line == month) {
         // copy all existing data
         while (getline(PersonInfo, current_line) && (current_line != next_month)) {
            if(!PersonInfo.good()) {
               cerr << i << ".2. Error: Unable to read from the record file!" << endl;
            }
            file_lines.push_back(current_line);
         }
         // insert our stuff (before the next month begins)
         file_lines.push_back(whole_name);
         if(!PersonInfo.good()) {
            cerr << i << ".3. Error: Unable to read from the record file!" << endl;
         }
         // don't forget the next month's line, if there was one
         if (PersonInfo) {
            if(!PersonInfo.good()) {
               cerr << i << ".4. Error: Unable to read from the record file!" << endl;
            }
            file_lines.push_back(current_line);
         }
      }

      if(PersonInfo.eof()) {
         if(!PersonInfo.good()) {
            cerr << i << ".5. Error: Unable to read from the record file!" << endl;
         }
         break;
      }
      if(!PersonInfo.good()) {
            cerr << i << ".6. Error: Unable to read from the record file!" << endl;
      }
   }
if(!PersonInfo.good()) {
      cerr << "Error: Unable to complete reading from the record file!" << endl;
   }
   
   PersonInfo.seekp(ios::beg);
   for (deque <string> ::iterator p_current_line = file_lines.begin(); p_current_line != file_lines.end(); p_current_line++) {
   PersonInfo << *p_current_line << '\n'; }
   
   PersonInfo.close();
   
   if(!PersonInfo.good()) {
      cerr << "Error: Unable to close the record file!" << endl;
   }
   return(true);


... That means the file is not good after the algorithm is complete but before I do anything else... which makes no sense to me =(
Topic archived. No new replies allowed.