In the following simple program,

int main()
{
    string name;
    char sex;
    cout << "enter M or F: ";
    cin.get(sex);
    cout << "enter name: ";
    [B]getline(cin, name);[/B]
    cout << name << " is " << sex << endl;
    return 0;
}

after the user typed in a character "M" or space, then the program is finished and it seems the value of "name" is taken to be the ENTER that was pressed after typing in "M". So far, the only thing I could do is either

(a) not to enter any character, but only press ENTER when prompted to enter "sex"--which is of course not desired result; or,

(b) change the "getline(cin, name)" to "cin >> name", which also is not desired (can't take spaces within the string).

Is there a way to fix this? Many thanks for your time!

Recommended Answers

All 5 Replies

>cin.get(sex);
This only reads one of the two characters that are in the stream. The second character is '\n'. Conveniently enough, getline uses '\n' as a delimiter, so it's terminating successfully right away rather than blocking for input.

Read this thread for more details on what the problem is and how to fix it. The latter half of the thread is very advanced, so you can just read the first part if you don't care.

Thank you so much for the quick help! It did fix it!

One more question: this happens only to getline(cin, name), but not to cin >> name, right? I could get it run with the latter w/o problem before fixing.

Also, no difference whether it's cin.get(sex) or cin>>sex.

Standard input in C++ is buffered (nonstandard methods are available to get unbufffered input if it's needed, but that can limit the portability of your program). This means when your program obtains data via the key board or from a file or from wherever else, the data is entered character by character into a holding area (called a buffer) until you push the enter/newline key, which is also stored in the buffer. Once the newline char is entered, the information in the buffer is used by the istream method/operator called to put information in the input buffer into the variables passed to the method/operator.

The different istream methods/operators work differently. For example, get() has several different different versions. However, the versions have in common that they don't remove from the input stream/buffer whatever they don't put into the variable they are using. If get() only reads in a single char then anything else in the buffer is left in the buffer. If get() uses a char array to store input then the delimiting char is left in the input buffer, if one is found.

Likewise, >> leaves the terminating char, and any subsequent char in the input buffer, in the input buffer.

getline() on the other hand removes any delimiting char it finds from the buffer.

Therefore, mixing get() or >> with getline() in any given program can lead to unexpected results unless you know the input stream/buffer is in the appropriate state at all times. If you know all calls to getline() occur before any call to get() or >>, then everything should be safe. In practice that's difficult to gaurantee, however. So clearing the input stream/buffer before any calls to getline() is important in programs that mix input methods. Alternatively, never using anything but getline() to obtain input is a viable answer to the problem as well.

It isn't C++ that's buffered, it is the device you are reading. C++ doesn't care either way. When the standard input is from the keyboard, it is buffered. If it is redirected from, say, a file, it is not buffered. (By "buffered" of course I mean "line-buffered".) Either way it should not affect the functioning of your program (that is, this thread's specific program, as well as most other C++ homework style programs.)

I am an advocate of the "get everything from the user using getline()" way of thinking.
Interactively (that is, when line-buffered), the user enters everything one line at a time.
Non-interactively (that is, when piping from a file or other stream device), the input is still read one line at a time. Therefore, since all input is expected one line at a time no matter what, just read input one line at a time.

Once you have a line from the user, you can test/convert/whatever it and more gracefully recover from input errors. However, this involves a little bit of overhead. You could also just test the fail and bad bits after trying to read input, and pay attention to those unread characters that Lerner talked about.

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

This code assumes that the user entered correct input for at least the first non-whitespace character on the line, removes anything else on the line and continues happily.

Argh.

>this happens only to getline(cin, name), but not to cin >> name, right?
Correct. The >> operator skips leading whitespace by default, and because '\n' is considered whitespace, this particular problem is solved by using it. However, there's a difference between getline and the >> operator for string input in that the >> operator stops reading at whitespace as well, but getline has a default delimiter of '\n'. So >> will read a word, and getline will read a line. The functionality is different.

>It isn't C++ that's buffered, it is the device you are reading.
C++ is buffered too. There are (typically) two layers of buffering:

  1. The shell layer, which is under the system's control, and manages how input is taken from an input device and sent to your program. Consoles are typically line oriented, where input is collected by the console shell until it sees a newline character. When that happens, the whole collection is sent to the C++ runtime for processing.
  2. The C++ runtime layer, which is under your C++ implementation's control, and manages how often lower level input requests are made.

The C++ buffer is strictly for performance, so if you change your stream to be unbuffered, you won't suddenly be able to read a single character from the console without pressing the [Return] key. It's the shell buffer that controls this behavior, which is why the only way to read a character without pressing [Return] is to use a non-standard solution that changes how the shell buffers characters.

Stream I/O is a lot more complicated both in implementation and usage than most people think. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.