Correct way to use cin.fail()
cin.fail()
returns true if the last cin command failed, and false otherwise.
An example:
int main() {
int i, j = 0;
while (1) {
i++;
cin >> j;
if (cin.fail()) return 0;
cout << "Integer " << i << ": " << j << endl;
}
}
Now suppose you have a text file - input.txt and it's contents are:
30 40 50 60 70 -100 Fred 99 88 77 66
When you will run above short program on that, it will result like:
Integer 1: 30
Integer 2: 40
Integer 3: 50
Integer 4: 60
Integer 5: 70
Integer 6: -100
it will not continue after 6th value as it quits after reading the seventh word, because that is not an integer: cin.fail()
returns true
.
Understanding cin.fail() and cin.clear - Vector Appending Program
After entring the first vector inputs write Ctrl+Z
(By your keyboard instead of a number) for windows or Ctrl+D
for linux to make cin.fail()
be true
to stop the loop taking inputs and after entring the second vector inputs too.
Note that the vectors will repeat the last entries because you make push_back(a)
after detecting cin.fail()
. So, you can avoide that by changing
a_vct.push_back(a);
to
if(cin.good()) a_vct.push_back(a);
C++ cin.fail() question
You need to clear the line from cin, using cin.ignore, in addition to clearing the stream state (which is what cin.clear does).
I have several utility functions to make this easier (you'll be interested in clearline in particular, which clears the stream state and the current line) and almost an exact example of what you want.
Your code, more or less, using my clearline:
#include "clinput.hpp" // move my file to a location it can be used from
int main() {
using namespace std;
while (true) {
cout << "Enter a number (0 to exit): ";
int number;
if (cin >> number) {
cout << "Read " << number << '\n';
if (number == 0) {
break;
}
}
else {
if (cin.eof()) { // tested only *after* failed state
cerr << "Input failed due to EOF, exiting.\n";
return 1;
}
cerr << "Input failed, try again.\n";
clearline(cin); // "cin >> clearline" is identical
}
}
return 0;
}
There is still a potential issue here (fixed in my clinput_loop.cpp with blankline), with leaving input in the buffer that will screw up later IO (see "42 abc" in the sample session). Extracting the above code into a separate and self-contained function is left as an exercise for the reader, but here's a skeleton:
template<class Type, class Ch, class ChTr>
Type read(std::basic_istream<Ch,ChTr>& stream, Ch const* prompt) {
Type value;
// *try input here*
if (could_not_get_input or more_of_line_left) {
throw std::runtime_error("...");
}
return value;
}
template<class Type, class Ch, class ChTr>
void read_into(
Type& value,
std::basic_istream<Ch,ChTr>& stream,
Ch const* prompt
) {
value = read<Type>(stream, prompt);
}
Example use:
int n;
try {
read_into(n, std::cin, "Enter a number: ");
}
catch (std::runtime_error& e) {
//...
raise;
}
cout << "Read " << n << '\n';
clearline function extracted for posterity, in case above links ever break (and slightly changed to make self-contained):
#include <istream>
#include <limits>
template<class C, class T>
std::basic_istream<C,T>& clearline(std::basic_istream<C,T>& s) {
s.clear();
s.ignore(std::numeric_limits<std::streamsize>::max(), s.widen('\n'))
return s;
}
The template stuff is a bit confusing if you're not used to it, but it's not hard:
- std::istream is a typedef of
std::basic_istream<char, std::char_traits<char> >
- std::wistream is a typedef of
std::basic_istream<wchar_t, std::char_traits<wchar_t> >
- widen allows
'\n'
to becomeL'\n'
as appropriate - this code works for both of the common char and wchar_t cases, but also any compatible instantiation of basic_istream
- it's written to be called as
clearline(stream)
orstream >> clearline
, compare to other manipulators like std::endl, std::ws, or std::boolalpha
C++ cin.fail() executes and moves to next line even though input is of another data type
From your comments, it appears you want integer input exclusively and do not want to allow input of additional character after the integer like 1w
, even though 1
would be converted to an int
while leaving w
unread to be removed by .ignore(...)
after your call to .clear()
. (as mentioned above, your use of .clear()
and .ignore(...)
are now correct.
If this is your intent, you need a way to check if there is anything else following the integer input by the user. To do that in a non-blocking way if nothing actually exists, you have a couple of options. (e.g. you can .peek()
at the next character -- but you only get one, or you can use a line-oriented input approach) The line-oriented approach allows you to read the entire line of user input into a string
and then extract the integer value and check whether there is anything else contained in the line of input.
The most straight forward way is to create a std::basic_stringstream from the line of data read with getline()
. This approach, consumes the entire line of user input and provides you with all the tools you need to extract whatever information you may want from the line. It also does this in a way that does not effect any of your subsequent user inputs.
While I would recommend you combine your void menu()
and void options(int input)
functions so you simply have one function to handle input processing for your menu -- there is nothing wrong with breaking it in two other than the possibility of a few lines of code being duplicated. The following is just a suggestion on how to handle your menu()
function to only allow integer input. You can adapt it to the remainder of your code.
You will need a couple of additional includes:
#include <sstream>
#include <string>
I would also #define
the first and last acceptable menu entries so you have those constants available in your code in a place that can be easily changed as you add to your menu, e.g.
#define MENUFIRST 1 /* first valid entry */
#define MENULAST 1 /* last valid entry */
(note: that will allow only 1
be entered as a valid menu entry)
To limit you menu()
function using the approach outlined above, you could do:
void menu(){
int user_input = 0;
string line, unwanted;
for (;;) { /* loop continually until valid input received */
cout << "\nEnter 1 to print something: ";
if (!getline (cin, line)) { /* read an entire line at a time */
cerr << "(user canceled or unrecoverable stream error)\n";
return;
}
stringstream ss (line); /* create a stringstream from line */
if (!(ss >> user_input)) { /* test if valid integer read */
/* test eof() or bad() */
if (ss.eof() || ss.bad()) /* if not, did user cancel or err */
cerr << "(empty-input or unreconverable error)\n";
else if (ss.fail()) /* if failbit - wasn't an int */
cerr << "error: invalid integer input.\n";
}
else if (ss >> unwanted) { /* are there unwanted chars? */
cerr << "error: additional characters following user_input.\n";
user_input = 0; /* reset user_input zero */
} /* was int outside MENUFIRST-to-MENULAST? */
else if (user_input < MENUFIRST || MENULAST < user_input)
cerr << "error: integer not a valid menu selection.\n";
else /* valid input!, break read loop */
break;
}
options(user_input);
}
The comments should be self-explanatory given the discussion above, but let me know if you have questions. Using the function with the rest of your code (and commenting the unused // bool fail;
), you can test whether it meets your requirements, e.g.
Example Use/Output
$ ./bin/menu_int
Enter 1 to print something: w1
error: invalid integer input.
Enter 1 to print something: $#%&^#&$ (cat steps on keyboard) !#$%%^%*()
error: invalid integer input.
Enter 1 to print something: 1w
error: additional characters following user_input.
Enter 1 to print something: -1
error: integer not a valid menu selection.
Enter 1 to print something: 24
error: integer not a valid menu selection.
Enter 1 to print something: 1
Enter the amount of cake
3
3
Also note, your menu()
funciton will now properly trap a manual EOF
generated by the user pressing Ctrl+d (or Ctrl+z on windows) to cancel input and exit gracefully.
loop involving cin.fail() running multiple times
You need use ignore
with the overloaded one, see this anser here.
Or you can just need to run getline
to drain the contents, but this way is slower and unnecessary.
#include <iostream>
#include <string>
int main()
{
double n;
while( std::cout << "Please, enter a number\n"
&& ! (std::cin >> n) )
{
std::cin.clear();
std::string line;
std::getline(std::cin, line);
std::cout << "I am sorry, but '" << line << "' is not a number\n";
}
std::cout << "Thank you for entering the number " << n << '\n';
}
cin.fail not working as expected in do while
On the first iteration, cin.ignore();
means that the first character will be ignored. Stepping through with a debugger would have shown you that cin.ignore()
actually blocks until you provide an input. If your input is a single digit int
, then that value will be ignored. Then, std::cin
will wait until you give another value. The loop only executes once in that case, but you have to provide two values, so it looks like it executes twice.
The solution would be to only ignore if there is an error. You should also probably ignore the whole input line with cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
rather than just ignore
a single character. Try the following instead :
#include <ios>
#include <iostream>
#include <limits>
int main()
{
int q1 = 1;
std::cin >> q1;
while (std::cin.fail()){
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> q1;
}
std::cout << q1;
return 0;
}
Set cin.fail() manually
You can set failbit
using setstate
:
Example
#include <iostream>
#include <sstream>
int main()
{
int a;
std::cin >> a;
if(a > 10)
std::cin.setstate(std::ios_base::failbit);
if (std::cin.fail()) {
std::cout << "fails\n";
}
}
Related Topics
How to Sort an Stl Map by Value
Check Variadic Templates Parameters for Uniqueness
Too Many Initializers for 'Int [0]' C++
C++11: How to Alias a Function
Why Can't a Derived Class Call Protected Member Function in This Code
Measure Execution Time in C++ Openmp Code
How to Use Visual Studio Code to Compile Multi-Cpp File
Differencebetween Const_Iterator and Non-Const Iterator in the C++ Stl
Simple Way to Unzip a .Zip File Using Zlib
C++ Function Pointer (Class Member) to Non-Static Member Function
Does the 'Offsetof' MACro from <Stddef.H> Invoke Undefined Behaviour
How to Disassemble a Binary Executable in Linux to Get the Assembly Code
Performance of Qsort VS Std::Sort
How to Define a C++ Preprocessor MACro Through the Command Line with Cmake