Pregunta

So, I was writing this question in c++

    cout << "Would you like the answer in Joules or eV?" << endl;
    cout << "Type 'j' or 'e' to make the selection" << endl;
    invalid = true;
    while (invalid) { //This while loop repeats the question until the user enters e or j
        invalid = false;
        getline(cin, units);
        if (units == "e"|| units == "E") {
            answer = energy(Z,n1,n2);
            cout << "The answer is: " << answer << " eV" << endl;
        }
        else if (units == "j"|| units == "J" || units == "joules" || units == "joule") {
            answer = energy_j(Z,n1,n2);
            cout << "The answer is: " << answer << " J" << endl;    
        }
        else {
            cout << "Please enter either j or e, and press enter." << endl;
            invalid = true;
        }
    }

and it seemed fine, but for some reason it always prints the "else" bit when I run it. I have the exact same code below and it runs fine. Can anyone help? (I'm compiling with g++ on linux, if that makes a difference)

The code runs fine, but I'd like to know about why this small bug is happening. The output is shown below:

Would you like the answer in Joules or eV?
Type 'j' or 'e' to make the selection
Please enter either j or e, and press enter.
k
Please enter either j or e, and press enter.
e

Edit: So you can see how variables are defined, etc. Link to full code here

¿Fue útil?

Solución

The problem:

The last extraction you performed before the call to std::getline() was:

while (!(cin >> n2))
{
    cout << "Please enter a number for the final state" << endl;
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}

Which is perfectly fine. The only problem is that, given a valid extraction, the newline character '\n' will be left in the stream. By default, the unformatted input function std::getline() delimits input upon the acquisition of a newline character. If the residual newline is still left in the stream, the input cannot be performed.

Note that technically std::getline() discards the character but ceases extraction once it is found.


The solution:

The solution I suggested in the comments section of your question was to execute the following lines of code above your unformatted extraction:

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

What this does in order is clear the underlying stream state (a bitmask that represents errors during I/O) and ignore the maximum amount of characters until the next newline character is found (it also consumes the newline).

Why did I suggest this?

If the stream state has a bit turned on (either failbit, eofbit, or badbit) the stream won't be able to perform I/O (this also includes ignoring characters). The ignore() call is used to discard any residual input until we reach the end of the line, so we have a fresh new line to restart input.

However, since you updated your post showing me the full code, I now realize that those two calls are not needed as you already took care of invalid input in the first code example I showed you. Moreover, since you made sure the most recent extraction succeeded, there's no need to reset the stream state.

Instead, what I propose is to use the manipulator std::ws which will discard all whitespace characters (newline is also considered whitespace) until a non-whitespace character is found. It is a much more idiomatic approach to discarding newlines:

std::getline(std::cin >> std::ws, units);
//           ^^^^^^^^^^^^^^^^^^^

This should be equivalent to the ignore() call because all that's left in the stream is a newline.

Improvements:

Here are a few:

  1. The first is to always check if your input succeeded:

    if (std::getline(std::cin >> std::ws, units))
    {
        //
    }
    
  2. If units will always be one character, then just use a character:

    char units;
    if (std::cin >> units)
    {
        if (units == 'e' || ... )
    }
    

Otros consejos

Try with std::cin.clear(); before the getline.

Maybe print out units before the if to see what it is holding. I would personally do the following to get input :

    string units;
    cin >> units;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top