Modify infomation in file

Hi, could anyone explain to me why the value that I change by using Modify() function does not replace the previous value? It will also occur a lot of meaningless line after that? Assuming that I already have an account by using CreateAccount() and save it to file by using SaveToFile()

Thanks a lot!

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
void SearchAndModifyAccount(int account_number)
{
	bool found = false;
	Account account;
	fstream file;

	file.open("account.dat", ios::binary | ios::in | ios::out);
	if (!file)
	{
		cout << "Khong mo duoc file, hay an phim enter de quay lai menu" << endl;
		system("pause");
		return;
	}

	while (!file.eof() && found == false)
	{
		while (file.read(reinterpret_cast<char*> (&account), sizeof(Account)))
		{
			// compare account_number with that read from the file
			if (account.GetAccountNumber() == account_number) {
				account.Modify();
				const std::streampos pos = file.tellg();
				file.write(reinterpret_cast<char*> (&account), sizeof(Account));
				found = true;
			}
		}

	}

	file.close();
	if (found == false)
		cout << "\n\nKhong tim thay tai khoan";
}


1
2
3
4
5
6
7
8
9
10
11
void Account::Modify()
{
	cout << "\nNhap thong tin de thay doi:";
	cout << "\nTen chu tai khoan: ";
	cin.ignore();
	cin.getline(name_, 100);
	cout << "Loai tai khoan: ";
	cin >> type_;
	cout << "So du: ";
	cin >> balance_;
}


1
2
3
4
5
6
7
8
9
10
11
12
void Account::CreateAccount()
{
	cout << "Nhap so tai khoan: ";
	cin >> account_number_;
	cout << "Nhap ten chu tai khoan: ";
	cin.ignore();
	cin.getline(name_, 100);
	cout << "Nhap loai tai khoan: ";
	cin >> type_;
	cout << "So du ban dau: ";
	cin >> balance_;
}


1
2
3
4
5
6
7
8
9
10
void SaveToFile(Account &account)
{
	ofstream out_file;
	out_file.open("account.dat", std::ios_base::binary | std::ios_base::app);
	if (out_file.is_open())
	{
		out_file.write(reinterpret_cast<char*> (&account),sizeof(Account));
	}
	out_file.close();
}
Last edited on
Not currently.
#mbozzi

Sorry but what does it mean?
When I posted that there was no code to accompany your question, so when I posted that there was no way for anyone to help you.

At a glance, it looks like you've never called seekp to place the put cursor in the appropriate place before attempting to write the modified record.

Note that the streampos on line 22 is collected after the read has completed, so it is offset in the stream by sizeof account. It would be simplest to call tellg before the read, and use the result as an argument to seekp.
#mbozzi

I apologize for that, cuz I don't know how to show my source code so I create it and edit it after it. One more time, Sorry about that!

I did it 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
void SearchAndModifyAccount(int account_number)
{
	bool found = false;
	Account account;
	fstream file;

	file.open("account.dat", ios::binary | ios::in | ios::out);
	if (!file)
	{
		cout << "Khong mo duoc file, hay an phim enter de quay lai menu" << endl;
		system("pause");
		return;
	}

	while (!file.eof() && found == false)
	{
		const std::streampos pos = file.tellg();
		while (file.read(reinterpret_cast<char*> (&account), sizeof(Account)))
		{
			// compare account_number with that read from the file
			if (account.GetAccountNumber() == account_number) {
				account.Modify();
			}
		}
		file.seekp(pos);
		file.write(reinterpret_cast<char*> (&account), sizeof(Account));
		found = true;
		
	}

	file.close();
	if (found == false)
		cout << "\n\nKhong tim thay tai khoan";
}


But seem like it didn't print it out in "account.dat". So I wonder what wrong in my logic here?
> But seem like it didn't print it out in "account.dat". So I wonder what wrong in my logic here?
When in doubt, prototype!

Whenever you're stuck, or are unsure about a new concept, then creating a mini side project just to explore is quick and easy. It's certainly a lot quicker and easier than trying to analyse it deep within some larger project.

Create the most basic Account possible (just an ID) and try your various modes of operation.
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

class Account {
  int m_id;
public:
  int setID(int id) { m_id = id; }
  int getID() { return m_id; }
};

int main()
{
  const int nRec = 5;
  Account account;
  fstream file;

  file.open("account.dat", ios::binary | ios::in | ios::out);
  if ( !file.is_open() ) {
    cerr << "Oops" << endl;
    return 1;
  }

  // Writing
  for ( int i = 0 ; i < nRec ; i++ ) {
    account.setID(i);
    if ( file.write(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
      cout << "Wrote record " << i << endl;
    } else {
      cerr << "Write fail" << endl;
    }
  }
  file.flush();   // Flush between write and read
  cout << "Read Pos=" << file.tellg() << endl;
  cout << "Write Pos=" << file.tellp() << endl;

  // Reading
  file.seekg(0);
  for ( int i = 0 ; i < nRec ; i++ ) {
    if ( file.read(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
      cout << "Account" << i << "=" << account.getID() << endl;
    } else {
      cerr << "Read 1 fail" << endl;
    }
  }
  cout << "Read Pos=" << file.tellg() << endl;
  cout << "Write Pos=" << file.tellp() << endl;

  // Finding and updating a specific record
  file.seekg(0);
  while ( file.read(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
    cout << "R=" << account.getID() << endl;
    if ( account.getID() == nRec/2 ) {
      account.setID(42);
      file.seekp(-sizeof(Account),ios_base::cur);
      file.write(reinterpret_cast<char*>(&account), sizeof(Account));
      file.flush(); // Flush between write and read
    }
  }

  // Reading again
  file.clear();   // Because the while loop may have run into end of file
  file.seekg(0);
  for ( int i = 0 ; i < nRec ; i++ ) {
    if ( file.read(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
      cout << "Account " << i << "=" << account.getID() << endl;
    } else {
      cerr << "Read 2 fail" << endl;
    }
  }

  return 0;
}



$ g++ foo.cpp
$ ./a.out 
Wrote record 0
Wrote record 1
Wrote record 2
Wrote record 3
Wrote record 4
Read Pos=20
Write Pos=20
Account0=0
Account1=1
Account2=2
Account3=3
Account4=4
Read Pos=20
Write Pos=20
R=0
R=1
R=2
R=3
R=4
Account 0=0
Account 1=1
Account 2=42
Account 3=3
Account 4=4
#salem c

So I do like this in the Modify() function
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
void SearchAndModifyAccount(int account_number)
{
	bool found = false;
	Account account;
	fstream file;

	file.open("account.dat", ios::binary | ios::in | ios::out);
	if (!file)
	{
		cout << "Khong mo duoc file, hay an phim enter de quay lai menu" << endl;
		system("pause");
		return;
	}

	while (!file.eof() && found == false)
	{
		file.seekg(0);
		const std::streampos pos = file.tellg();
		while (file.read(reinterpret_cast<char*> (&account), sizeof(Account)))
		{
			// compare account_number with that read from the file
			if (account.GetAccountNumber() == account_number) {
				char choice;
				account.PrintAccount();
				cout << "\nDo you want to change info or keep?";
				cin >> choice;
				if (choice == 'Y')
				{
					account.Modify();
					file.seekp(sizeof(Account), ios_base::cur);
					file.seekp(pos);//for writing positioning
					file.write(reinterpret_cast<char*>(&account), sizeof(Account));
					file.flush();
					found = true;
				}
				else if (choice == 'N')
				{
					Menu();
				}
			}
		}

	}

	file.close();
	if (found == false)
		cout << "\n\nKhong tim thay tai khoan";
}


So it will not only modify the previous info but also replace it? Am I right? I already check it and it work when just one account. But if more than one, for example 3 accounts. If I try to change the info of the account, it will change the first one? Is it because of file.seekg(0);

P/s: But if I remove the else if() it work normally
Last edited on
I guess that salem c's code is a bit confusing...

Is it because of file.seekg(0);
Yes, remove line 17. Line 30 doesn't make sense either.


But seem like it didn't print it out in "account.dat".
What made you think so?
#coder777

But seem like it didn't print it out in "account.dat".
What made you think so?


Actually it is just my doubt about it so I don't have any hypothesis then I just told what I took just because I did not see anything error in debugger.

Yes, remove line 17. Line 30 doesn't make sense either.

Why line 30 makes no sense? I think it calls the Modify() function, doesn't it?

1
2
3
4
5
6
7
8
9
10
11
void Account::Modify()
{
	cout << "\nNhap thong tin de thay doi:";
	cout << "\nTen chu tai khoan: ";
	cin.ignore();
	cin.getline(name_, 100);
	cout << "Loai tai khoan: ";
	cin >> type_;
	cout << "So du: ";
	cin >> balance_;
}
This is what happens if you don't seek.
1
2
3
4
5
6
7
8
9
  while ( file.read(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
    cout << "R=" << account.getID() << endl;
    if ( account.getID() == nRec/2 ) {
      account.setID(42);
      //file.seekp(-sizeof(Account),ios_base::cur);
      file.write(reinterpret_cast<char*>(&account), sizeof(Account));
      file.flush(); // Flush between write and read
    }
  }

Account 0=0
Account 1=1
Account 2=2
Account 3=42
Account 4=4


Compare with
1
2
3
4
5
6
7
8
9
  while ( file.read(reinterpret_cast<char*>(&account), sizeof(Account)) ) {
    cout << "R=" << account.getID() << endl;
    if ( account.getID() == nRec/2 ) {
      account.setID(42);
      file.seekp(-sizeof(Account),ios_base::cur);
      file.write(reinterpret_cast<char*>(&account), sizeof(Account));
      file.flush(); // Flush between write and read
    }
  }

Account 0=0
Account 1=1
Account 2=42
Account 3=3
Account 4=4



1
2
3
4
5
while (!file.eof() && found == false)
	{
		file.seekg(0);
		const std::streampos pos = file.tellg();
		while (file.read(reinterpret_cast<char*> (&account), sizeof(Account)))

1. Two nested while loops are wrong. You only need one while loop to examine every record in the file.
2. your 'pos' is both declared const, and also set up once.

> file.seekp(pos);//for writing positioning
So when you do this, you always overwrite the FIRST record.

1
2
3
4
else if (choice == 'N')
				{
					Menu();
				}

No, don't call Menu() recursively, just return back to it.
#salem c why do we need a minus before sizeof(Account) in file.seekp in line 5? Do we have any reason for that?

file.seekp(-sizeof(Account),ios_base::cur)
I totally overlooked that there are two loops as salem c pointed out. Yes, the inner while loop doesn't make sense.

Replace the while on line 19 with an if. The else for that if should cout an error.

Why line 30 makes no sense? I think it calls the Modify() function, doesn't it?
Line 30 is the unnecessary file.seekp(sizeof(Account), ios_base::cur);.
#coder777

if I change it with if statement, it just not reach the account that I want( for example account 3).
Seriously... rather than simply telling us things about how your program works, why don't you investigate it yourself? Why don't you step through it in your debugger, and look at what's going on, and see why it's not reaching the account you want.
if I change it with if statement, it just not reach the account that I want( for example account 3).
The inner loop is just wrong. It makes pos invalid. So change:
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
	while (!file.eof() && found == false)
	{
		file.seekg(0);// !!!
		const std::streampos pos = file.tellg();
		while if (file.read(reinterpret_cast<char*> (&account), sizeof(Account))) // !!!
		{
			// compare account_number with that read from the file
			if (account.GetAccountNumber() == account_number) {
				char choice;
				account.PrintAccount();
				cout << "\nDo you want to change info or keep?";
				cin >> choice;
				if (choice == 'Y')
				{
					account.Modify();
					file.seekp(sizeof(Account), ios_base::cur);
					file.seekp(pos);//for writing positioning
					file.write(reinterpret_cast<char*>(&account), sizeof(Account));
					file.flush();
					found = true;
				}
				else if (choice == 'N')
				{
					Menu();
				}
			}
		}

	}
#coder777
Thanks a lot for yoru help! I think that it is the same method as the function that you explained to me yesterday. If my question mind you so much, just tell me and I will improve it. One more time, thank you for that.

#MikeyBoy
Sorry if my question bother that much. I apologize!
> #salem c why do we need a minus before sizeof(Account) in file.seekp in line 5? Do we have any reason for that?
Yes, you read post http://www.cplusplus.com/forum/general/262310/#msg1133822 where I show what happens when you leave it out, and the subsequent change in which record gets overwritten.

Sorry if my question bother that much. I apologize!

It's that there are tools you could learn to use that would help you discover the reasons why your code isn't behaving the way you want it to, and help you discover how to fix it, if only you'd learn to use them.

Don't you want to be able to fix your own problems?

Don't you want to be able to be a better programmer?
Topic archived. No new replies allowed.