-1

I'm doing a crud in C++, but in the data entry, I can't add space in the name, it bugs, I tried to use ignore and getline, it worked but the first letter disappears when reading the variable, using only getline, it doesn't work

class banca
{
private:
    char name[100];
    char email[100];
    char course_aluno[100];
    int semestre;
    int id;
    
public:
    friend istream& operator>>(istream& in, banca& p);
    friend ostream& operator<<(ostream& out, banca& p);
}

istream& operator>>(istream& in, banca& p)
{
    cout << "--------ALUNO--------\n";
    cout << "Name: \n" << endl;
    cin.ignore();
    cin.getline(p.name, 100);
    cout << "Email: \n";
    cin.ignore();
    cin.getline(p.email, 100);
    cout << "Course : \n";
    cin.ignore();
    cin.getline(p.course_aluno, 100);
    cout << "Semestre: \n";
    in >> p.semestre;
    cout << "ID: \n";
    in >> p.id;
}
Chris
  • 26,361
  • 5
  • 21
  • 42
Alanis
  • 11
  • 6
    Why are you doing `ignore` _before_ you've encountered data that you need to discard? – Nathan Pierson Jul 07 '22 at 03:37
  • 2
    Also why are you, for the most part, using `std::cin` in your `operator>>` instead of the argument `in`? – Nathan Pierson Jul 07 '22 at 03:39
  • was just testing, with the in da in the same, and the function ignore because it was the only way i was able to enter the data – Alanis Jul 07 '22 at 03:46
  • 1
    For that matter, you probably don't want to write your `operator>>` in such a way that it always displays a prompt to the user. Especially if you're planning to use this to read from CSVs or something where it's not even supposed to be pulling from an interactive terminal. You might want to make a factory function that prints prompts, but that probably doesn't need to be a concern of `operator>>` itself. – Nathan Pierson Jul 07 '22 at 03:46
  • If I take your existing code, [fix those problems](https://godbolt.org/z/88x9ze1vh), and fill in a few other blanks with what I consider to be reasonable guesses, I get something that appears to work as intended. (Because of the way Godbolt works, the values I enter to standard input aren't displayed as output, but you can see that it's successfully reading all the things I place stdin.) This question would benefit from a minimal _reproducible_ example. – Nathan Pierson Jul 07 '22 at 03:49
  • 1
    Recommendation: Place the `ignore` after the operation that leaves the garbage in the stream. If you `ignore` before some other operation, odds are really good you'll find cases where you get to the `ignore` without garbage you need removed and throw out something you needed to parse. – user4581301 Jul 07 '22 at 03:50
  • Always check the stream state after an operation. There's no point to trying to get the e-maill address of the rest of the data if the read for the name failed and made the stream unreadable until the error is acknowledged and fixed.. – user4581301 Jul 07 '22 at 03:52
  • In this case, by the way, the operation that leaves garbage in the stream is `in >> p.id;`. So an ignore _after_ that could be appropriate. Every other ignore is misplaced. [Expanded demo](https://godbolt.org/z/eaWExssjo), where I've added `in >> std::ws;` to clear the newline left behind from `in >> p.id;` and added a second `banca` object so that you can see that this version works when you read more than one `banca` in sequence. – Nathan Pierson Jul 07 '22 at 03:54
  • my code is in portuguese, works but the name don't can write [my code](https://godbolt.org/z/s59njcGG7) – Alanis Jul 07 '22 at 04:14
  • 3
    Unrelated: Be VERY careful of the combination of `#include ` and `using namespace std;`. Prefer not to use either ([why](https://stackoverflow.com/q/1452721/4581301) and [why](https://stackoverflow.com/q/31816095/4581301)), but the first includes the entire C++ Standard library and the second places the entire C++Standard library into the global namespace where it will come into conflict with all of the identifiers you declare. Things can get really weird, really fast. – user4581301 Jul 07 '22 at 04:48

1 Answers1

1

You are basically running into an old problem, when working with input functions.

You need to learn about the difference between formatted and unformatted input.

The extraction and inserter operators >> and << are formatted input functions. They read or write a sequence of characters and format them. Example:

int i = 42;
std::cout << i;

This is an example of formatted IO. Although 42 has the binary representation of 101010 you will see the character sequence "42" on the screen.

Same is valid vice versa. If you want to read a number from the user, you typically use something like

int i;
std::cin >> i;

And, although you enter the characters '4' and '2' and ENTER ('\n') in the terminal, you will get the value 42 in your variable "i". The ENTER ('\n') is still in the input stream and has not yet been consumed. This is an additional problem that I will explain in a minute.

If you want to read a string, like "Peter Parker", and use

std::string name{};
std::cin >> name;

You will only get "Peter", because the formatted input functions (per default) stop converting after they hit a white space. So, after "Peter" has been read. The whitespace will not be consumed and will be still in the input stream.

For this reason you will use std::getline to read a string. This will not stop at a whitespace, but at a delimiter, per default at '\n'. So it will read a complete line.

From reading above, you may now see the problem. If there is a transition from formatted to unformatted input there is often the problem that there is still a whitespace (often the ENTER '\n', in your case the space) in the input stream. And you need to eliminate this whitespace.

You tried it with std::ignore, but there is a better possibility: std::ws. You may read here about it. This will often be the better approach. You may use it after the transition from formatted to unformatted input the eat away the non wanted white spaces. You can also use it in general if you want to eat up leading white spaces using unformatted input. (Beware: the formatted input functions like >> will skip the white spaces (per default)).

ignore is a unformatted input function and will actively wait for characters. It is an input function. It will not return until it read something. So, be careful. It will often be just used in error cases.

With the above knowledge, we could rewrite your code like the below:

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

class banca
{
private:
    std::string name;
    std::string email;
    std::string course_aluno;
    int semestre;
    int id;

public:
    friend std::istream& operator >>(std::istream& in, banca& p);
    friend std::ostream& operator <<(std::ostream& out, const banca& p);
};

std::istream& operator>>(std::istream& in, banca& p)
{
    std::cout << "\n\n--------ALUNO--------\n";
    std::cout << "Name:\n";
    std::getline(in >> std::ws, p.name);

    std::cout << "Email:\n";
    std::getline(in, p.email);

    std::cout << "Course:\n";
    std::getline(in, p.course_aluno);

    std::cout << "Semestre:\n";
    in >> p.semestre;

    std::cout << "ID:\n";
    in >> p.id;
    return in;
}
std::ostream& operator <<(std::ostream& out, const banca& p) {
    return out << "\nName:\t\t " << p.name << "\nEMail:\t\t " << p.email << "\nCourse:\t\t "
        << p.course_aluno << "\nSemestre:\t " << p.semestre << "\nID:\t\t " << p.id << '\n';
}

int main() {
    banca b1,b2;

    std::cin >> b1;
    std::cout << b1;
    std::cin >> b2;
    std::cout << b2;
}

But beware. Input functions may fail. Check the return code of each input function. And if you have an error, then use ignore.

Example. If you expect a number and enter a string, then the formatted input function will fail, and all invalid characters will still be in the stream. Enter "abc" for the semester and you will see it.

Solution. Check result of IO function:

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <limits>

class banca
{
private:
    std::string name{};
    std::string email{};
    std::string course_aluno{};
    int semestre{};
    int id{};

public:
    friend std::istream& operator >>(std::istream& in, banca& p);
    friend std::ostream& operator <<(std::ostream& out, const banca& p);
};

std::istream& operator>>(std::istream& in, banca& p)
{
    std::cout << "\n\n--------ALUNO--------\n";
    std::cout << "Name:\n";
    std::getline(in >> std::ws, p.name);

    std::cout << "Email:\n";
    std::getline(in, p.email);

    std::cout << "Course:\n";
    std::getline(in, p.course_aluno);

    std::cout << "Semestre:\n";
    in >> p.semestre;

    std::cout << "ID:\n";
    in >> p.id;
    return in;
}
std::ostream& operator <<(std::ostream& out, const banca& p) {
    return out << "\nName:\t\t " << p.name << "\nEMail:\t\t " << p.email << "\nCourse:\t\t "
        << p.course_aluno << "\nSemestre:\t " << p.semestre << "\nID:\t\t " << p.id << '\n';
}

int main() {
    banca b1,b2;

    if (std::cin >> b1)
        std::cout << b1;
    else {
        std::cerr << "\nError: Problem with input\n\n";
        std::cin.clear(); // unset failbit
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
    }
    if (std::cin >> b2)
        std::cout << b2;
    else {
        std::cerr << "\nError: Problem with input\n\n";
        std::cin.clear(); // unset failbit
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
    }
}

A M
  • 14,694
  • 5
  • 19
  • 44