Using Getline(Cin, S) After Cin

Using getline(cin, s) after cin [duplicate]

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.ignore()) 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.

Using cin AFTER getline(cin, string)

I understand that using std::cin>> leaves an '\n' at the end so I have to use cin.ignore()

The first part is true. The second part is true only if you want to follow it up with a call to getline.

However, you have calls to cin.ignore() even before the first call to std::cin >>. That will expect you to enter lines, which could be empty lines but lines nontheless, the lines will be read and discarded.

Replace the lines:

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

with just

getline(cin, name);

Also a little bit later, you have:

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

You don't need the call to cin.ignore() here. Remove it.

Using `getline(cin, s);` after using `cin n;`

You need to clear the input stream - try adding the following after your cin:

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

The accepted answer to this question gives a good explanation of why/when this is required.

How does cin.ignore work with cin and getline?

cin >> string2 looks for the beginning of the next word, and skips over the newline that was left in the buffer after cin >> string1. So at this point ,only the second newline is still in the buffer, and cin.ignore() skips past it.

More detailed, if you type:

line1
line2
line3

The buffer contains:

line1\nline2\nline3\n
^

The ^ shows the current position in the buffer. At the beginning, it's at the beginning of the buffer. After you do cin >> string1, it looks like:

line1\nline2\nline3\n
^

When you do cin >> string2, it finds the beginning of the next word (the next l character), and after it's done the buffer looks like:

line1\nline2\nline3\n
^

Then cin.ignore() skips past the next newline, so it looks like:

line1\nline2\nline3\n
^

And now when you call getline(), it reads the line with line3.

Why do cin and getline exhibit different reading behavior?

reading behavior of cin and getline.

cin does not "read" anything. cin is an input stream. cin is getting read from. getline reads from an input stream. The formatted extraction operator, >>, reads from an input stream. What's doing the reading is >> and std::getline. std::cin does no reading of its own. It's what's being read from.

first cin read up until the "\n". once it hit that "\n", it increments the
cursor to the next position

No it doesn't. The first >> operator reads up until the \n, but does not read it. \n remains unread.

The second >> operator starts reading with the newline character. The >> operator skips all whitespace in the input stream before it extracts the expected value.

The detail that you're missing is that >> skips whitespace (if there is any) before it extracts the value from the input stream, and not after.

Now, it is certainly possible that >> finds no whitespace in the input stream before extracting the formatted value. If >> is tasked with extracting an int, and the input stream has just been opened and it's at the beginning of the file, and the first character in the file is a 1, well, the >> just doesn't skip any whitespace at all.

Finally, std::getline does not skip any whitespace, it just reads from the input stream until it reads a \n (or reaching the end of the input stream).

A real solution to the 'cin' and 'getline' issue

Like this:

std::string line, maybe_an_int;

if (rand() == 1)
{
if (!(std::getline(std::cin, maybe_an_int))
{
std::exit(EXIT_FAILURE);
}
}

if (!(std::getline(std::cin, line))
{
std::exit(EXIT_FAILURE);
}

int a = std::stoi(maybe_an_int); // this may throw an exception

You can parse the string maybe_an_int in several different ways. You could also use std::strtol, or a string stream (under the same condition as the first if block):

    std::istringstream iss(maybe_an_int);
int a;
if (!(iss >> a >> std::ws) || iss.get() != EOF)
{
std::exit(EXIT_FAILURE);
}

You could of course handle parsing errors more gracefully, e.g. by running the entire thing in a loop until the user inputs valid data.

Issue When Clearing cin Buffer After Using cin.getline()

You can use istream::gcount() to decide whether there are any more characters other than '\n' left in the line still.

Here are the cases you need to think about.

  1. The return value of cin.gcount() is less than SIZE-1. In this case, there is nothing left in the line. You don't have to worry about ignoring the rest of the line.

  2. The return value of cin.gcount() is SIZE-1. This could be due to two scenarios.

    1. The user enters SIZE-2 characters followed by a newline. In this case, there is nothing left in the line. You don't have to worry about ignoring the rest of the line.

    2. The user enters SIZE or more number of characters followed by a newline. In this case, there are still some characters left in the line. You will want to ignore the rest of the line.

  3. The return value of cin.gcount() is SIZE. This can happens only when the user enteres SIZE-1 characters followed by a newline. All the characters from the line are read into the argument provided to the function. The newline character is read and discarded. You don't have to worry about ignoring the rest of the line.

Give the above cases, the only time you have to worry about ignoring the rest of the line is when you run into case 2.2. That condition is met when cin.gcount() == SIZE-1 and strlen(words) == SIZE-1.

void getString(char* words) {
cout << "Enter your string: ";
cin.getline(words, SIZE);
if (cin.gcount() == SIZE-1 && strlen(words) == SIZE-1)
{
// There are characters in the stream before the \n.
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}


Related Topics



Leave a reply



Submit