pauliwago pauliwago - 3 months ago 48
C++ Question

Using getline(cin, s) after cin

I need the following program to take the entire line of user input and put it into string names:

cout << "Enter the number: ";
int number;
cin >> number;

cout << "Enter names: ";
string names;

getline(cin, names);


With the
cin >> number
command before the
getline()
command however (which I'm guessing is the issue), it won't allow me to input names. Why?

I heard something about a
cin.clear()
command, but I have no idea how this works or why this is even necessary.

Answer
cout << "Enter the number: ";
int number;
if (cin >> number)
{
    // throw away the rest of the line 
    char c;
    while (cin.get(c) && c != '\n')
        if (!std::isspace(c))
        {
            std::cerr << "ERROR unexpected character '" << c << "' found\n";
            exit(EXIT_FAILURE);
        }
    cout << "Enter names: ";
    string name;
    // keep getting lines until EOF (or "bad" e.g. error reading redirected file)...
    while (getline(cin, name))
        ...use name...
}
else
{
    std::cerr << "ERROR reading number\n";
    exit(EXIT_FAILURE);
}

In the code above, this bit...

    char c;
    while (cin.get(c) && c != '\n')
        if (!std::isspace(c))
        {
            std::cerr << "ERROR unexpected character '" << c << "' found\n";
            exit(EXIT_FAILURE);
        }

...checks the rest of the input line after the number contains only whitespace.

Why not just use ignore?

That's pretty verbose, so using ignore on the stream after >> x is an oft-recommended alternative way to discard content through to the next newline, but it risks throwing away non-whitespace content and in doing so, overlooking corrupt data in the file. You may or may not care, depending on whether the file's content's trusted, how important it is to avoid processing corrupt data etc..

So when would you use clear and ignore?

So, std::cin.clear() (and std::cin.igore()) isn't necessary for this, but is useful for removing error state. For example, if you want to give the user many chances to enter a valid number.

int x;
while (std::cout << "Enter a number: " &&
       !(std::cin >> x))
{
    if (std::cin.eof())
    {
        std::cerr << "ERROR unexpected EOF\n";
        exit(EXIT_FAILURE);
    }

    std::cin.clear();  // clear bad/fail/eof flags

    // have to ignore non-numeric character that caused cin >> x to
    // fail or there's no chance of it working next time; for "cin" it's
    // common to remove the entire suspect line and re-prompt the user for
    // input.
    std::cin.ignore(std::numeric_limits<std::streamsize>::max());
}

Can't it be simpler with skipws or similar?

Another simple but half-baked alternative to ignore for your original requirement is using std::skipws to skip any amount of whitespace before reading lines...

if (std::cin >> number >> std::skipws)
{
    while (getline(std::cin, name))
        ...

...but if it gets input like "1E6" (e.g. some scientist trying to input 1,000,000 but C++ only supports that notation for floating point numbers) won't accept that, you'd end up with number set to 1, and E6 read as the first value of name. Separately, if you had a valid number followed by one or more blank lines, those lines would be silently ignored.

Comments