C++ Fstream SEEKg/Getline not working!

Hi guys,

Im trying to create a change password from file function but my seekg getline for:

1
2
3
      int pos = tempUser.length() + tempPass.length()+2; // pos for beginning of the line
      openFile.seekg(-pos, ios::cur); 
      getline(openFile, hold);


Is not working. I want it to go to the beginning of that line and capture the entire line in HOLD.

Edit:
I need a fix for the seekg guys, mainly anything related to fstream/ostream. I cant use maps for that part, for the sake of learning.

Here is the full 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
47
48
49
50
51
52
53
54
55
56
void AccountManager::changePassword(AccountManager & account) {
  string  username, password, newPass, passwordConf, tempUser, tempPass, hold;
  fstream openFile("UserPass.txt", ios_base::out | ios_base::in | ios_base::app);

  // / Check if username exsists.
  do {
    cout << "Enter your username: " << endl;
    getline(cin, username);
    cout << "Enter you current password: " << endl;
    getline(cin, password);

    if (account.UserPass[username] != password) {
      cout << "Username and password do not match. " << endl;
    }
  } while (account.UserPass[username] != password);

  do {
    cout << "Enter new password: " << endl;
    getline(cin, newPass);
    cout << "Retype password: " << endl;
    getline(cin, passwordConf);

    if (newPass != passwordConf) {
      cout << "Password does not match confirmation. " << endl;
    }
  } while (newPass != passwordConf);

  // /find / replace password with newPass in file

  while (!openFile.eof()) {
    getline(openFile, tempUser, ';');
    getline(openFile, tempPass);

    if ((tempUser == username) && (tempPass == password)) {
      fstream openFile("UserPass.txt", ios_base::out | ios_base::in);
      int pos = tempUser.length() + tempPass.length()+2; // pos for beginning of the line
      openFile.seekg(-pos, ios::cur); // Go back from current position by x amount
      getline(openFile, hold);
      char *  cPass = new char[newPass.length()];

      strcpy(cPass, newPass.c_str());

      int index = hold.find(';')+1;    // changes pass in file at index ;+1

      openFile.seekp(index);
      openFile.write(cPass, tempPass.length());
      cout << "Password has been changed. " << endl;
      switchLog(account);                // Login on successful password change.


      //delete[] cPass breaks program
      break;
    }
  }
  account.UserPass[username] = newPass;
}


Ty
Last edited on
Try ios_base instead of ios.

On another note, how are you accessing an array location with a string?
account.UserPass[username]
its a map not an array.

The iosbase didnt help =/
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
void AccountManager::changePassword(AccountManager & account) {

  string username, password ;
  // / Check if username exsists.
  do {
    cout << "Enter your username: " << endl;
    getline(cin, username);
    cout << "Enter you current password: " << endl;
    getline(cin, password);

    if( account.UserPass.find(username) == account.UserPass.end() || // is the username in the map?
        account.UserPass[username] != password) { // does the password match?
      cout << "Username and password do not match. " << endl;
    }

  } while (account.UserPass[username] != password);

  string newPass, passwordConf ;
  do {
    cout << "Enter new password: " << endl;
    getline(cin, newPass);
    cout << "Retype password: " << endl;
    getline(cin, passwordConf);

    if (newPass != passwordConf) {
      cout << "Password does not match confirmation. " << endl;
    }
  } while (newPass != passwordConf);

  account.UserPass[username] = newPass; // update the map

  // /find / replace password with newPass in file
  {
      ofstream openFile("UserPass.txt") ; // truncate and overwrite the file
      for( const auto& pair : account.UserPass ) // for each username, password pair in the map
        openFile << pair.first << ';' << pair.second << '\n' ; // write them to the file
  }
}
I already have an initializer for the map, I really just need a way to fix my current problem.

The map is only there just in case the file fails to open, I dont want to use a map for this (trying to learn files etc).

Just need to fix the seekg and ill be good to go.
1. We can't reliably seek to an arbitrary position in a stream opened in text mode.

2. The new password may be longer than the old password.
You open "UserPass.txt" twice. Once at line 3 and once at line 35.
So how do we actually seek a position?

Im fine if the new pass is longer since the file is set up as:

USERNAME;PASSWORD

So there is really nothing it can overwrite.
Last edited on
> So how do we actually seek a position?

In a file stream opened in text mode, we can reliably seek to a valid position returned by an earlier call to a positioning function (say returned by tellg()).


> Im fine if the new pass is longer since the file is set up as:
> USERNAME;PASSWORD
> So there is really nothing it can overwrite.

Original content:

USERNAME1;PASSWORD1<newline>USERNAME2;PASSWORD2<newline>USERNAME3;PASSWORD3<newline>

Content after seeking to and overwriting PASSWORD2 with LONGERPASSWORD2:

USERNAME1;PASSWORD1<newline>USERNAME2;LONGERPASSWORD2AME3;PASSWORD3<newline>

Something like this, perhaps:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// find / replace password with newPass in file

// 1. refresh the map from the file (if required)
{
  string tempUser, tempPass ;
  ifstream openFile("UserPass.txt" ) ; // open for input
  if(openFile)
  {
      account.UserPass.clear() ;
      while( getline(openFile, tempUser, ';' ) && getline(openFile, tempPass ) )
          account.UserPass[tempUser] = tempPass ;
  }
}

// 2. update the map with newPass
account.UserPass[username] = newPass;

// 3. recreate (truncate and overwrite) the file
{
  ofstream openFile("UserPass.txt") ; // output, truncate
  for( const auto& pair : account.UserPass ) // for each username, password pair in the map
    openFile << pair.first << ';' << pair.second << '\n' ; // write them to the file
}
Hi

This is my initialize function:

1
2
3
4
5
6
7
8
9
10
void AccountManager::InitializeMap(AccountManager & account) {
  std::string hold;

  fstream openFile("UserPass.txt", ios::out | ios::in | ios::app);

  while (getline(openFile, hold)) {
    account.UserPass.insert(make_pair(hold.substr(0, hold.find(';')), hold.substr(hold.find(';') + 1)));
    hold.clear();
  }
}


Your basically doing the same thing but re-initializing from file by checking the map all over again correct?

From my understanding is that its not worth using seekg just because the margin of error.

I do like how you approached this, mind showing me how you would implement tellg with this? I just need to know why seek/tell isnt working in general (for learning purposes).

I will more than likely go with your approach (less room for errors).

I appreciate your help on this, spent over 8 hrs today trying to debug this and nothing works.

ty

EDIT:
Just tried your code... puts me to shame lol. Works great ty.
Last edited on
> mind showing me how you would implement tellg with this?
> I just need to know why seek/tell isnt working in general (for learning purposes).

In general, avoid seek and tell (except to seek to the beginning) with file streams opened in text mode.

In this particular case, the file is opened in append mode.
If the file is opened in append mode, all output to the file is done at the current end of the file, regardless of intervening repositioning. Even if you modify the file position to a position before the file's end, you cannot write there.
http://stdcxx.apache.org/doc/stdlibug/30-3.html#3031-2
k removed app and it now works, but I get why we shouldnt write directly with seek now (overwrite the next line if the new pass is longer, etc).

you can marked this solved.

ty
Topic archived. No new replies allowed.