41

I'm attempting to get a few user-input parameters from the console, two strings, two ints and a double. The relevant code I'm trying to use is this:

#include <string>
#include <iostream>
using namespace std;

// ...

string inputString;
unsigned int inputUInt;
double inputDouble;

// ...

cout << "Title: "; 
getline(cin, inputString);
tempDVD.setTitle(inputString);

cout << "Category: "; 
getline(cin, inputString);
tempDVD.setCategory(inputString);

cout << "Duration (minutes): "; 
cin >> inputUInt; 
tempDVD.setDuration(inputUInt);

cout << "Year: "; 
cin >> inputUInt; 
tempDVD.setYear(inputUInt);

cout << "Price: $"; 
cin >> inputDouble; 
tempDVD.setPrice(inputDouble);

However, when running the program, instead of waiting for the first inputString to be entered, the code doesn't stop until the second getline() call. Thus the console output looks like this:

Title: Category:

with the cursor appearing after category. If I input now, the program then jumps ahead to the year input, not allowing me to enter more than one string. What's happening here?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
user754852
  • 503
  • 1
  • 4
  • 10
  • Cannot reproduce; please post real, complete code. I bet your problem lies elsewhere. Also, don't mix formatted input and `getline()`. – Kerrek SB Oct 16 '11 at 20:18
  • @KerrekSB: I agree that mixing them is a symptom of bad style, but what is the objective reason for not mixing them? I think it is perfectly valid to mix them, really. – sehe Oct 16 '11 at 20:21
  • @sehe: The problem is that the formatted extraction doesn't consume the newlines, so it's very easy to get unexpected results when you do `getline()` after thinking that you already processed all of the preceding line. I'm not saying it's impossible, but it often makes the logic very hard to read and maintain. – Kerrek SB Oct 16 '11 at 20:22
  • Sorry for the incomplete code; the program was lengthy. I think now that the problem was related to a previous use of a cin to get an int before this block. – user754852 Oct 16 '11 at 20:30

9 Answers9

39

The problem is you are mixing calls to getline() with the use of the operator >>.

Remember that operator >> ignored leading white space so will correctly continue across lines boundaries. But stops reading after the input has successfully been retrieved and thus will not swallow trailing '\n' characters. Thus if you use a getline() after a >> you usually get the wrong thing unless you are careful (to first remove the '\n' character that was not read).

The trick is to not use both types of input. Pick the appropriate one and stick to it.

If it is all numbers (or objects that play nice with operator >>) then just use operator >> (Note string is the only fundamental type that is not symmetric with input/output (ie does not play nicely)).

If the input contains strings or a combination of stuff that will require getline() then only use getline() and parse the number out of the string.

std::getline(std::cin, line);
std::stringstream  linestream(line);

int  value;
linestream >> value;

// Or if you have boost:
std::getline(std::cin, line);
int  value = boost::lexical_cast<int>(line);
Martin York
  • 257,169
  • 86
  • 333
  • 562
21

You need to flush the input buffer. It can be done with cin.clear(); cin.sync();.

Marc Plano-Lesay
  • 6,808
  • 10
  • 44
  • 75
  • 10
    Strangely for me it doesn't work with the combination of those. It only works with `std::cin.ignore()`. – KeyC0de Aug 07 '18 at 01:18
20

You can use

cin.ignore();

or as @kernald mentioned use

cin.clear();
cin.sync();

before using getline()

Hossein
  • 24,202
  • 35
  • 119
  • 224
4

Use cin.clear() as mentioned and use proper error handling:

    cin.clear();
    cin.sync();

    cout << "Title: "; 
    if (!getline(cin, inputString))  exit 255;
    tempDVD.setTitle(inputString);

    cout << "Category: "; 
    if (!getline(cin, inputString))  exit 255;
    tempDVD.setCategory(inputString);

    cout << "Duration (minutes): "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setDuration(inputUInt);

    cout << "Year: "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setYear(inputUInt);

    cout << "Price: $"; 
    if (!(cin >> inputDouble)) exit 255; 
    tempDVD.setPrice(inputDouble);
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 2
    Thanks. The code only works if both cin.clear() and then cin.sync() are used. – user754852 Oct 16 '11 at 20:26
  • 1
    The easiest way to mix the >> operator with the getline() method is to enter cin.ignore() before any call to getline so the input buffer is cleared of any left over new line characters from the previous input. – Corey Zambonie Jun 24 '15 at 16:46
2

If user inputs a space before \n in previous cin before getline, only ignore itself wouldn't be enough so you have to use this code instead of ignore() alone. For example 12345 \t \n will not work anymore. All unprocessed characters must be ignored.

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

Use this between cin and getline.

Farbod Ahmadian
  • 728
  • 6
  • 18
2

Also works with ws. You can use getline(cin >> ws, inputString)) to eat the whitespaces or the newlines after reading data with cin command.

Osman Coskun
  • 121
  • 1
  • 7
0

Mixing getline() with input streams in generally a bad thing to do. It's theoretically possible to manually handle the dirty buffers left over by using streams, but it's an unnecessary pain that should definitely be avoided.

You are better off using a console library to grab your input, this way the dirty-work can be abstracted for you.

Take a look at TinyCon. You can just use the static method tinyConsole::getLine() in replace of your getline and stream calls, and you can use it as many times as you'd like.

You can find information here: https://sourceforge.net/projects/tinycon/

Unix-Ninja
  • 124
  • 2
0

cin.sync(); use this instead of cin.ignore( works best.

Gurudev
  • 33
  • 1
  • 1
  • 6
-1

The function_______ will return the last read character and will move the inside pointer one with -1 char.

  1. Getline()
  2. Peek() 3.flush() 4.putback()