-2

Please keep in mind I am new to C++ and coding The goal of this project is to teach myself how to read and write files using c++. I decided to make an account login screen. Also, this is very unfinished. My goal is to read one line at a time and store each line into a variable. Ex. Line 1 into the username variable. Right now, using the getline function I receive the error code: "no instance of overloaded function matches the argument list argument types are: (std::ofstream, std::string)". I keep running into errors with the getline function and I was wondering how to fix this scenario, and if getline works as intended, will the line by line reading function work.

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main()
{   
    
    cout << "  Create an Account\n\n";
    cout << "Enter a username: ";
    string newUsername;
    cin >> newUsername;
   
    cout << "Enter a password: ";
    string newPassword;
    cin >> newPassword;

    ofstream file("user_list.txt");
    file << newUsername << "\n" << newPassword << "\n" << "true";

    ifstream file("user_list.txt");

    string username;
    string password;
    string trueOrFalse;

    while (getline(file, username)
        && getline(file, password)
        && getline(file, trueOrFalse))
    {
        // All three items were read successfully.
        cout << username << " has the password " << password
            << " and mystery item " << trueOrFalse << endl;
    }
    
}



CM1233
  • 11
  • 3
  • 5
    [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) – Some programmer dude Aug 24 '23 at 04:40
  • "I cannot use the getline function to read a line." Why is that? – n. m. could be an AI Aug 24 '23 at 04:58
  • "When I don't really even understand what EOF is?" Well, if you read the link, you'll see an explanation of what EOF signifies, and then an explanation of why that's probably not what you want. You may also find [this question](https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction) relevant, it covers mixing `>>` and `getline` – Nathan Pierson Aug 24 '23 at 05:11

3 Answers3

1

There's a very simple reason why std::getline doesn't work for you...

ofstream file(...);

And

getline(file, ...);

You try to read from an output stream. That's not possible. You need to read from an input stream.

Please don't mix output and input using the same file. I recommend you also separate writing to the file, and reading from the file, into separate functions.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

The "o" in ofstream means "out"; you can't read from it.
Use two streams, one in and one out, and make sure that it's closed for writing before reading.
The destructor will take care of this for you.

Also, you should not loop on eof(), and there is no point in counting the lines.

Here is how you might do it:

int main()
{   
    cout << "  Create an Account\n\n";
    cout << "Enter a username: ";
    string newUsername;
    cin >> newUsername;
   
    cout << "Enter a password: ";
    string newPassword;
    cin >> newPassword;

    {
        // A separate scope makes sure that the file gets closed, but
        // it would be a good idea to put the entire account creation
        // in its own function.
        ofstream file("user_list.txt");
        file << newUsername << '\n' << newPassword << '\n' << "true";
    }
    
    ifstream file("user_list.txt");
    string username;
    string password;
    string trueOrFalse;

    // Loop over all users as an example.
    // There is no need for a separate string to copy in each step.
    while (getline(file, username)
        && getline(file, password)
        && getline(file, trueOrFalse))
    {
        // All three items were read successfully.
        cout << username << " has the password " << password 
             << " and mystery item " << trueOrFalse << endl;
    }
}

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

The answer given @Some programmer dude is the correct answer to your question. With that fix, you may be able to go on, and complete the task of reading a given line from a file.

What no one has said, however, is that, most likely, this is not going to help with the larger task that you describe in your question:

Look up a user name in a file, and, if found, get the matching password.

The code you have written shows you reaching for that, but my conclusion is that no one has taught you how to use stepwise refinement to break a large problem into a series of smaller ones. You seem to be tackling the whole thing all at once.

I use a method called stub programming where you call empty, do-nothing stub functions to perform the tasks you will code later. With stub programming, the first pass at this project might be:

Step 1
// main.cpp
#include <fstream>
#include <iostream>
#include <string>

void create_file(std::string const& file_name)
{
    std::cout << "Create File \n"
        "Enter user names and passwords to create the data file.\n\n";
    std::ofstream file(file_name);
    if (file.is_open())
    {
        std::cout << "File open for output...\n\n";
    }
}
int main()
{
    std::string file_name = "user_list.txt";
    create_file(file_name);
    return 0;
}

Function create_file is a stub. It has just enough code to compile without errors. I put a fake std::cout message so that I could tell whether the file was opened successfully. Later, I will delete this line of code.

Function main sets the file_name, and then calls function create_file, which presumably, will create a complete file of user names and passwords.

The big advantage of stub programming is that you can compile and run your program even though it is only half written. At all stages of develepment, you have a running program. Of course, because it is only half written, it cannot do what it is supposed to do, but it compiles and runs without errors.

Here is the output:

Create File
Enter user names and passwords to create the data file.

File open for output...

You can also open the file user_list.txt in text editor, and see what it looks like. At this stage, it should be an empty file, and, in fact, it is.

Step 2

In the next step, I am going code function get_line_from_cin. It takes a string variable prompt as its argument, displays it on the screen, and then calls std::getline to read one line from std::cin. I am using std::getline so that I can test for blank lines later on. My plan is to interpret blank lines to mean: "I'm done."

For now, let's use get_line_from_cin to ask for a user name. I'll put the call in create_file. And with this small amount of progress, it will be time to run the program again, and make sure there are no errors.

// main.cpp
#include <fstream>
#include <iostream>
#include <string>

std::string get_line_from_cin(std::string const& prompt)
{
    std::string line;
    std::cout << prompt;
    std::getline(std::cin, line);
    return line;
}
void create_file(std::string const& file_name)
{
    std::cout << "Create File \n"
        "Enter user names and passwords to create the data file.\n\n";
    std::ofstream file(file_name);
    if (file.is_open())
    {
        std::string user_name, password;
        user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
    }
}
int main()
{
    std::string file_name = "user_list.txt";
    create_file(file_name);
    return 0;
}

When the program asked for a user name, I entered PizzaMan, and this is the output I got:

Create File
Enter user names and passwords to create the data file.

Enter user name. Press Enter on a blank line when done: PizzaMan

Of course, the name was not written to the file, and the program stopped even though I did not press Enter on a blank line, but that is what I expected.

Step 3

Now that get_line_from_cin is working, I can use it to put some structure into function create_file. I am going to use while(!user_name.empty()) { ... } to start a loop. Inside the loop, I will ask the user to enter another user name. I am going to keep looping until the user presses Enter on a blank line.

void create_file(std::string const& file_name)
{
    std::cout << "Create File \n"
        "Enter user names and passwords to create the data file.\n\n";
    std::ofstream file(file_name);
    if (file.is_open())
    {
        std::string user_name, password;
        user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        while (!user_name.empty())
        {
            // Get next user name.
            user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        }
    }
}

Nice. Now the program continues prompting me to enter one user name after another, until I press Enter on a blank line.

Create File
Enter user names and passwords to create the data file.

Enter user name. Press Enter on a blank line when done: PizzaMan
Enter user name. Press Enter on a blank line when done: Cleo
Enter user name. Press Enter on a blank line when done: Hipster
Enter user name. Press Enter on a blank line when done:

In addition, because I pushed my program through the compiler once again, I had a chance to fix any errors I made. That includes simple things, like a forgotten semi-colon, and also the more complex errors I may have made. You want to compile often, so that you catch your errors early.

Step 4

Let's do the same thing with password, except this time, I will use an if statement instead of a while statement. I want to test the password, and do nothing if the password is empty.

void create_file(std::string const& file_name)
{
    std::cout << "Create File \n"
        "Enter user names and passwords to create the data file.\n\n";
    std::ofstream file(file_name);
    if (file.is_open())
    {
        std::string user_name, password;
        user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        while (!user_name.empty())
        {
            password = get_line_from_cin("Enter password. Press Enter on a blank line to cancel: ");
            if (!password.empty())
            {
                // Password is not empty.
                // Do something with password...
            }
            // Get next user name.
            user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        }
    }
}

Now I can enter user names and passwords in succession, until I press Enter on a blank line for user name. If I press Enter on a blank line for passward, the program cancels the current user name, and skips ahead to the next user. Perfect.

Create File
Enter user names and passwords to create the data file.

Enter user name. Press Enter on a blank line when done: PizzaMan
Enter password. Press Enter on a blank line to cancel: pepperoni
Enter user name. Press Enter on a blank line when done: Cleo
Enter password. Press Enter on a blank line to cancel:
Enter user name. Press Enter on a blank line when done: Hipster
Enter password. Press Enter on a blank line to cancel: plaid
Enter user name. Press Enter on a blank line when done:

The line spacing bothers me a little, so I will output a blank line after the user enters a password.

Step 5

Now it is time to finish function create_file. We have a user name. We have a password. All we need to do is write them to the file.

void create_file(std::string const& file_name)
{
    std::cout << "Create File \n"
        "Enter user names and passwords to create the data file.\n\n";
    std::ofstream file(file_name);
    if (file.is_open())
    {
        std::string user_name, password;
        user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        while (!user_name.empty())
        {
            password = get_line_from_cin("Enter password. Press Enter on a blank line to cancel: ");
            std::cout << '\n';
            if (!password.empty())
            {
                file << user_name << '\n'
                    << password << '\n';
            }
            user_name = get_line_from_cin("Enter user name. Press Enter on a blank line when done: ");
        }
    }
}

For my test, I entered three user names, but only two passwords.

Create File
Enter user names and passwords to create the data file.

Enter user name. Press Enter on a blank line when done: PizzaMan
Enter password. Press Enter on a blank line to cancel: pepperoni

Enter user name. Press Enter on a blank line when done: Cleo
Enter password. Press Enter on a blank line to cancel:

Enter user name. Press Enter on a blank line when done: Hipster
Enter password. Press Enter on a blank line to cancel: plaid

Enter user name. Press Enter on a blank line when done:

As expected, the user name with the blank password was not written to the file.

This is what the file looked like when I opened it in a text editor.

PizzaMan
pepperoni
Hipster
plaid

So that is how you write programs: one step at a time. Do not move on to the next step until the current step is completed, and is running. That is the key: the program runs from day 1.

When a function such as create_file is finished, you can put it to bed, and forget about it. That is the time to empty your brain bucket, and get ready to start working on the next stub.

Step 6

I am not going to finish this program, but I wanted to sketch out what might come next.

Now you want to go back to function main. What I'll do is ask the user to enter a user name, and then call a new stub function to look up the user_name, and get the password. If the user name is found in the file, I will return the password that goes with it. Otherwise, if the user name is not found in the file, I will return an empty password.

std::string look_up_password(std::string const& user_name, std::string const& file_name)
{
    std::string password;
    std::ifstream file(file_name);
    if (file.is_open())
    {
        // ...
    }
    return password;
}
int main()
{
    std::string file_name = "user_list.txt";
    create_file(file_name);
    std::string user_name, password;
    std::cout << "\n\nWelcome! Please enter your user name to log in.\n\n";
    user_name = get_line_from_cin("Enter user name. Press Enter on a blank line to cancel: ");
    if (!user_name.empty())
    {
        password = look_up_password(user_name, file_name);
        if (password.empty())
        {
            // user name not on file...
            // ...
        }
        else
        {
            // password found
            // ...
        }
    }
    return 0;
}

This was a difficult, but important step. In function main, I had to figure that one if statement belonged inside another. What I have done with my stubs is sketch out the logic of the program. The loops and if-statements do that. I also got to reuse function get_line_from_cin.

The next step is to work on function look_up_password. Leave main as it is, and keep working on look_up_password until it is finished.

This technique of stub-programming may be the single most-important lesson for a beginner to learn. I hope it will be useful to you.

tbxfreeware
  • 547
  • 1
  • 9