Click to See Complete Forum and Search --> : Changing a record in a text file.


LordBob
February 28th, 2006, 12:27 AM
I'm new here. I've been working on an assignment that performs a number of operations on a text file via fstream. In one option, the program retrives a single record out of a text file (using seekg()) and re-writes another record over it. My problem is, I can read the specified record from the stream, but the new record is still appended to the end of the file rather than overwriting the one chosen.

Here is the code. Note that I have removed much of it (read: the working parts) for simplicity.

class Person{
protected:
char fName[20];
char lName[30];
long age;
double wage;
int deleted;

public:
Person() {fName[0] = 'x'; lName[0] = 'x'; age = 1; wage = 1.0; deleted = 0;} // constructor

void getinfo(){ //- prompts for, gets input and sets deleted to 0.

cout << "Enter First Name: "; cin >> fName;

cout << "Enter Last Name: "; cin >> lName;

cout << "Enter Age: ";
while(1){
while(!getlong(age))
cout << "**** Invalid integer **** - re-enter: ";

if(age > 0)
break;

cout << "Age must be greater than 0 - please re-enter: ";
}

cout << "Enter Wage: ";
while(1){
while(!getdoub(wage))
cout << "**** Invalid integer **** - re-enter: ";

if(wage > 0)
break;

cout << "Wage must be greater than 0 - please re-enter: ";
}

deleted = 0;
}

void dispinfo(){ //- displays data contained in class data members except deleted
if(deleted == 0){
cout << "First Name: " << fName << ", "
<< "Last Name: " << lName << ", "
<< "Age: " << age << ", "
<< "Wage: " << wage << ", "
<< "Deleted: ";
if (deleted == 1)
cout << "yes";
else
cout << "no";

cout << endl;
}
}

int dDeleted(){ //- simply returns the contents of the deleted data member
return deleted;
}

void deleteIt(){ //- simply sets the data member deleted to 0.
deleted = 0;
}

void unDeleteIt(){ //- simply sets the data member deleted to 1.
deleted = 1;
}
};


int main(){
Person pers; //create person variable
fstream f; //create stream

int a, b, r;
int endPos, count, pos;
char ans;

// FILE SETTINGS
cout << "Append to file, or recreate file?" << endl
<< "1. Append" << endl
<< "2. Recreate" << endl;
while(1){
while(!getint(b))
cout << "**** Invalid integer **** - re-enter: ";

if (b == 1 || b == 2)
break;
else
cout << "Pick a number 1-2: ";
}

if(b == 1)
f.open("person.txt", ios::in|ios::out|ios::binary|ios::app);
else if(b == 2)
f.open("person.txt", ios::in|ios::out|ios::binary|ios::trunc);
else{
cout << "Error: ending program";
return 0;
}
// FILE SETTINGS


// MAIN PROGRAM LOOP
while(1){

// MENU
cout << "1. Read a record" << endl
<< "2. Change a record" << endl
<< "3. Delete a record" << endl
<< "4. Undelete a record" << endl
<< "5. Total record count on file" << endl
<< "6. Exit program" << endl << endl;

cout << "Enter Choice -> ";
// This part works. Removed for simplicity.


switch (a){ // MENU CHOICES
// Read a record
case 1:
//This part works. Removed.
break;

/////////////////////////////////////////
///////// START BAD CODE /////////
case 2: // Change a record
f.clear();
f.seekg(0, ios::end); // set to end.
endPos = f.tellg();
count = endPos / sizeof(Person); //cout << count;
//cout << "Record count: " << count << endl;

cout << "Change record number: ";
while(1){
while(!getint(r))
cout << "**** Invalid integer **** - re-enter: ";

if(r > 0 && r <= count)
break;

cout << "Pick a number 1-" << count << ": ";
}
f.clear(); // clears the data stream
f.seekg(((r - 1) * sizeof(pers)), ios::beg); // moves to 2nd record - 0=1, 1=2
//*** READING THE RECORD WORKS JUST FINE...
f.read((char*)&pers, sizeof(pers)); //reads info at seeked position

cout << "\n\nHere is the info of record 2: \n";
pers.dispinfo(); // shows data in record
cout << "\nEnter Info to change below\n";
pers.getinfo(); //gets new data
f.clear(); // clears the data stream of errors
// *** I've tried both seekg() and seekp(), as shown below. Neither work here.
f.seekg(((r - 1) * sizeof(pers)), ios::beg); f.tellg(); // repositions to 2nd record - 0=1, 1=2
f.seekp(((r - 1) * sizeof(pers)), ios::beg); f.tellp(); // repositions to 2nd record - 0=1, 1=2
// *** This writes to the END of the file, not to the position I need.
f.write((char*)&pers, sizeof(pers)); // writes the record
cout << endl << "record has been re-written";

break;

//cases 3 - 6 removed:


} // END MENU CHOICES
} // END MAIN PROGRAM LOOP
f.close();
getch();
return 0;
}

int getint(int & getnum)
{
cin >> getnum;
if( cin.good()){
cin.ignore(10, '\n');
return 1;
}
cin.clear();
cin.ignore(10, '\n');
return 0;
}

The sad part is, I copied the code right out of another, working cpp file provided to us by the instructor, only changing the variable and class function names to suit my needs. Here is the original:

//**** going to change the 2nd record
f.clear(); // clears the data stream
f.seekg((1 * sizeof(p)), ios::beg); // moves to 2nd record - 0=1, 1=2
//*** remember counting starts at 0 not 1!
f.read((char*)&p, sizeof(p)); //reads info at seeked position

cout << "\n\nHere is the info of record 2: \n";
p.showdata(); // shows data in record
cout << "\nEnter Info to change below\n";
p.getdata(); //gets new data
f.clear(); // clears the data stream of errors
f.seekg((1 * sizeof(p)), ios::beg); // repositions to 2nd record - 0=1, 1=2
f.write((char*)&p, sizeof(p)); // writes the record
cout << endl << "record has been re-written";

This works perfectly. The only thing I'm really doing different is using append (opening my file for trunc kills the contents, making the code useless) and using it within a switch block.

I've been going over this for days now, and I can't figure it out. This is pretty much a last resort. Can anyone give me a hint?

treuss
February 28th, 2006, 02:51 AM
This works perfectly.Surprisingly, as the code sets the wrong pointer (seekg instead of seekp).The only thing I'm really doing different is using append (opening my file for trunc kills the contents, making the code useless)...Well, that's your problem. The ios::app bit translates to "(append) Seek to the end of the stream before each output operation." So, there you have it. Don't pass the ios::trunk bit but don't pas the ios::app bit either.

LordBob
February 28th, 2006, 12:20 PM
Surprisingly, as the code sets the wrong pointer (seekg instead of seekp).Oh, I agree. I thought it was wrong myself, but it worked fine. It's just what the professor gave us.

So, there you have it. Don't pass the ios::trunk bit but don't pas the ios::app bit either.Thanks a bunch. I'll try closing the file, then reopening without those bits.