1

I have .txt file and there are lines:

username1:123456789:etc:etc:etc:etc
username2:1234:etc:etc:etc:etc
username3:123456:etc:etc:etc:etc
username4:1234567:etc:etc:etc:etc

username1 - username; 123456789 - password; etc - more text.

I have code to read file and find line where is username what I need. Also code change password, but there is problem if new password is longer that old one then it looks like:

username3:11111111111tc:etc:etc:etc

if new password is shorter then it looks like:

username1:111111789:etc:etc:etc:etc

I have length of new password, but how can I get length of old one and replace it properly?

My code

include<iostream>
#include<fstream>
#include <cstring>

using namespace std;

int main() {
    int i=0;
    bool found = false;
    string line, username;
    char newpass[255] = "555555555555555";
    long length, plen;


    cout<<"Insert username: ";
    cin>>username;
    username+=":";
    fstream changeLINE("/.../testaDoc.txt");

    if (!changeLINE) {
        cout << "Can't find the file or directory!" << endl;
    }
    else

        while (getline(changeLINE, line) && !found) {
            i++;

            if (line.find(username) != string::npos) {
                length = changeLINE.tellg();
                changeLINE.seekg((length - line.length()) + username.length() - 1);
                changeLINE.write("", strlen(newpass));
                cout << line << " line " << i << endl;
                found = true;
            }
        }

    if (!found)
        cout << "User with username = " << username << " NOT FOUND!";

    changeLINE.close();

}

I'm working on Linux, writing in C++.

Edit

Maybe there are way to add string in text, but not replace it and also way to erase string by not replacing it? Then I could read length of old password compare it with new password and delete/add letters in string to replace it properly.

Trimidas
  • 97
  • 1
  • 1
  • 8
  • 4
    A file is just a contiguous stream of bytes, line breaks are just bytes in the file. You need to read the file into memory line-by-line modify the lines and write a new file. – Richard Critten Jun 20 '16 at 12:24
  • 2
    You may find it easier to read the file into a container, update the container and then write back to file. – NathanOliver Jun 20 '16 at 12:24
  • 1
    I'd suggest parsing the whole thing into a list of C structures. Update that list, then serialize back to a file. –  Jun 20 '16 at 12:25
  • Either rewrite the file from your change to the end (like inserting/deleting an element in the middle of a vector) or keep your passwords fixed-length (then you either need the current length or null characters at the end) – Nelfeal Jun 20 '16 at 12:26
  • You mean I need to write 2 files and then rewrite one? There are no way to do it same file just by replacing string in right place? – Trimidas Jun 20 '16 at 12:29
  • @Trimidas Think of it as a vector of `char`. How do you replace some of these with a different number of different `char`s in the middle of the vector ? You need to rewrite the whole thing (except maybe the part before the change). You don't need two files though. You can overwrite the one you already have. – Nelfeal Jun 20 '16 at 12:32
  • @Nelxiost I don't understand it a bit.. You mean to take it like and `array of char`? So if I have `string hello = "Hello!"` I can use it like `hello[0], hello[1]...` you mean that? – Trimidas Jun 20 '16 at 12:59
  • @Trimidas As Richard Critten said, a file is a contiguous stream of bytes. It is similar to a contiguous array of `char`, which, in C++, translates to `char[n]`, or better, `std::vector` (most of the time). When you use, for example, `std::vector::insert(pos, value)`, it needs to move every element placed after `pos`. Otherwise, there would be no room for the new element. The same thing is true for a file : if you want to insert a character in the middle of a file, you need to move the rest one place towards the end. – Nelfeal Jun 20 '16 at 13:12
  • Just a remark: It seems you are storing plain text passwords. This is not good practice. What you should store is the hash of the passwords, and coincidentally, such a hash has constant length, which would solve your particular problem. – Karsten Koop Jun 20 '16 at 14:02
  • yes, I will store hash of the password, but at the moment i'm testing code before use it. – Trimidas Jun 21 '16 at 11:49

1 Answers1

2

Unless the line you want to replace is the same length as the new one, you need a different strategy-

This is a strategy you can use:

  • Create temporary file
  • Write the lines you dont want to change straight to the temporary file. The line you want to change, you replace with the new line
  • close both files.
  • delete original file
  • rename temporary file to the original files name

Or you could as mentioned in the comments read all lines into memory, e.g. a vector of lines, replace the one you want to change, and then write all lines back into the file, replacing the former content. If the new line is shorter than the previous, you can trunctate the file using e.g: How to truncate a file while it is open with fstream

Could this work:

std::vector<std::string> lines;
while (std::getline(changeLINE, line)) 
{
    i++;

    if (line.find(username) != std::string::npos) {            
        std::cout << line << " line " << i << std::endl;
        found = true;
        std::string newline = username + ":" + newpass +     line.substr(line.find(":", username.length() + 2)) ;
        lines.push_back(newline);
    }
    else
    {
        lines.push_back(line);
    }
}

changeLINE.close();
std::ofstream ofs;
ofs.open("/.../testaDoc.txt", std::ofstream::out | std::ofstream::trunc);

for(auto& s: lines)
    ofs << s << std::endl;

ofs.close();
Community
  • 1
  • 1
thorsan
  • 1,034
  • 8
  • 19
  • I can't replace files... Because just now I'm working with `.txt` file, after I will work with system file so I don't want mess it up by creating and deleting files.. I need to replace line by using only one file. Just open file, replace old pass with new one and then save/close file.| I think there must be some simple way to do that, because I just need to replace some letters between `username:` and `:etc:etc:`, but not erase and lose other info in line.. – Trimidas Jun 20 '16 at 12:53
  • @Trimidas, then the second approach is the one you would like to use. – thorsan Jun 20 '16 at 12:56
  • yes, code work at the point - it change password properly. But there is problem that it make all text in one row. – Trimidas Jun 21 '16 at 12:01
  • I want to execute file in terminal, but got error in line `for(auto& s: lines)`. Error from terminal `error: ISO C++ forbids declaration of ‘s’ with no type [-fpermissive]` and `warning: range-based ‘for’ loops only available with -std=c++11 or -std=gnu++11` – Trimidas Jun 22 '16 at 08:22
  • Sorry my fault, I used gcc not g++... Completed code, now I can change linux password using c++. Thanks a lot again. – Trimidas Jun 22 '16 at 09:30