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 usecin.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.
The return value of
cin.gcount()
is less thanSIZE-1
. In this case, there is nothing left in the line. You don't have to worry about ignoring the rest of the line.The return value of
cin.gcount()
isSIZE-1
. This could be due to two scenarios.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.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.
The return value of
cin.gcount()
isSIZE
. This can happens only when the user enteresSIZE-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
"Unpacking" a Tuple to Call a Matching Function Pointer
Erasing Elements from a Vector
How to Parse a String to an Int in C++
How to Read an Entire File into a Std::String in C++
Convert Char to Int in C and C++
Why Would We Call Cin.Clear() and Cin.Ignore() After Reading Input
Does "Undefined Behavior" Really Permit *Anything* to Happen
Variable Number of Arguments in C++
How to Stop C++ Console Application from Exiting Immediately
How to Convert Between Big-Endian and Little-Endian Values in C++
Difference Between G++ and Gcc
Why Do Function Pointer Definitions Work With Any Number of Ampersands '&' or Asterisks '*'
Using Getline(Cin, S) After Cin
Why Should I Use a Pointer Rather Than the Object Itself
Cin and Getline Skipping Input