Most programs that have a user interface of some kind need to handle user input. In the programs that you have been writing, you have been using std::cin to ask the user to enter text input. Because text input is so free-form (the user can enter anything), it’s very easy for the user to enter input that is not expected.
As you write programs, you should always consider how users will (unintentionally or otherwise) misuse your programs. A well-written program will anticipate how users will misuse it, and either handle those cases gracefully or prevent them from happening in the first place (if possible). A program that handles error cases well is said to be robust.
In this lesson, we’ll take a look specifically at ways the user can enter invalid text input via std::cin, and show you some different ways to handle those cases.
std::cin, buffers, and extraction
In order to discuss how std::cin and operator>> can fail, it first helps to know a little bit about how they work.
When we use operator>> to get user input and put it into a variable, this is called an “extraction”. The >> operator is accordingly called the extraction operator when used in this context.
When the user enters input in response to an extraction operation, that data is placed in a buffer inside of std::cin. A buffer (also called a data buffer) is simply a piece of memory set aside for storing data temporarily while it’s moved from one place to another. In this case, the buffer is used to hold user input while it’s waiting to be extracted to variables.
When the extraction operator is used, the following procedure happens:
- If there is data already in the input buffer, that data is used for extraction.
- If the input buffer contains no data, the user is asked to input data for extraction (this is the case most of the time). When the user hits enter, a ‘\n’ character will be placed in the input buffer.
- operator>> extracts as much data from the input buffer as it can into the variable (ignoring any leading whitespace characters, such as spaces, tabs, or ‘\n’).
- Any data that can not be extracted is left in the input buffer for the next extraction.
Extraction succeeds if at least one character is extracted from the input buffer. Any unextracted input is left in the input buffer for future extractions. For example:
1 2 |
int x; std::cin >> x; |
If the user enters “5a”, 5 will be extracted, converted to an integer, and assigned to variable x. “a\n” will be left in the input stream for the next extraction.
Extraction fails if the input data does not match the type of the variable being extracted to. For example:
1 2 |
int x; std::cin >> x; |
If the user were to enter ‘b’, extraction would fail because ‘b’ can not be extracted to an integer variable.
Validating input
The process of checking whether user input conforms to what the program is expecting is called input validation.
There are three basic ways to do input validation:
- Inline (as the user types)
- Prevent the user from typing invalid input in the first place.
- Post-entry (after the user types)
- Let the user enter whatever they want into a string, then validate whether the string is correct, and if so, convert the string to the final variable format.
- Let the user enter whatever they want, let std::cin and operator>> try to extract it, and handle the error cases.
Some graphical user interfaces and advanced text interfaces will let you validate input as the user enters it (character by character). Generally speaking, the programmer provides a validation function that accepts the input the user has entered so far, and returns true if the input is valid, and false otherwise. This function is called every time the user presses a key. If the validation function returns true, the key the user just pressed is accepted. If the validation function returns false, the character the user just input is discarded (and not shown on the screen). Using this method, you can ensure that any input the user enters is guaranteed to be valid, because any invalid keystrokes are discovered and discarded immediately. Unfortunately, std::cin does not support this style of validation.
Since strings do not have any restrictions on what characters can be entered, extraction is guaranteed to succeed (though remember that std::cin stops extracting at the first non-leading whitespace character). Once a string is entered, the program can then parse the string to see if it is valid or not. However, parsing strings and converting string input to other types (e.g. numbers) can be challenging, so this is only done in rare cases.
Most often, we let std::cin and the extraction operator do the hard work. Under this method, we let the user enter whatever they want, have std::cin and operator>> try to extract it, and deal with the fallout if it fails. This is the easiest method, and the one we’ll talk more about below.
A sample program
Consider the following calculator program that has no error handling:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <iostream> double getDouble() { std::cout << "Enter a double value: "; double x; std::cin >> x; return x; } char getOperator() { std::cout << "Enter one of the following: +, -, *, or /: "; char op; std::cin >> op; return op; } void printResult(double x, char op, double y) { if (op == '+') std::cout << x << " + " << y << " is " << x + y << '\n'; else if (op == '-') std::cout << x << " - " << y << " is " << x - y << '\n'; else if (op == '*') std::cout << x << " * " << y << " is " << x * y << '\n'; else if (op == '/') std::cout << x << " / " << y << " is " << x / y << '\n'; } int main() { double x = getDouble(); char op = getOperator(); double y = getDouble(); printResult(x, op, y); return 0; } |
This simple program asks the user to enter two numbers and a mathematical operator.
Enter a double value: 5 Enter one of the following: +, -, *, or /: * Enter a double value: 7 5 * 7 is 35
Now, consider where invalid user input might break this program.
First, we ask the user to enter some numbers. What if they enter something other than a number (e.g. ‘q’)? In this case, extraction will fail.
Second, we ask the user to enter one of four possible symbols. What if they enter a character other than one of the symbols we’re expecting? We’ll be able to extract the input, but we don’t currently handle what happens afterward.
Third, what if we ask the user to enter a symbol and they enter a string like “*q hello”. Although we can extract the ‘*’ character we need, there’s additional input left in the buffer that could cause problems down the road.
Types of invalid text input
We can generally separate input text errors into four types:
- Input extraction succeeds but the input is meaningless to the program (e.g. entering ‘k’ as your mathematical operator).
- Input extraction succeeds but the user enters additional input (e.g. entering ‘*q hello’ as your mathematical operator).
- Input extraction fails (e.g. trying to enter ‘q’ into a numeric input).
- Input extraction succeeds but the user overflows a numeric value.
Thus, to make our programs robust, whenever we ask the user for input, we ideally should determine whether each of the above can possibly occur, and if so, write code to handle those cases.
Let’s dig into each of these cases, and how to handle them using std::cin.
Error case 1: Extraction succeeds but input is meaningless
This is the simplest case. Consider the following execution of the above program:
Enter a double value: 5 Enter one of the following: +, -, *, or /: k Enter a double value: 7
In this case, we asked the user to enter one of four symbols, but they entered ‘k’ instead. ‘k’ is a valid character, so std::cin happily extracts it to variable op, and this gets returned to main. But our program wasn’t expecting this to happen, so it doesn’t properly deal with this case (and thus never outputs anything).
The solution here is simple: do input validation. This usually consists of 3 steps:
1) Check whether the user’s input was what you were expecting.
2) If so, return the value to the caller.
3) If not, tell the user something went wrong and have them try again.
Here’s an updated getOperator() function that does input validation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
char getOperator() { while (true) // Loop until user enters a valid input { std::cout << "Enter one of the following: +, -, *, or /: "; char op; std::cin >> op; // Check whether the user entered meaningful input if (op == '+' || op == '-' || op == '*' || op == '/') return op; // return it to the caller else // otherwise tell the user what went wrong std::cout << "Oops, that input is invalid. Please try again.\n"; } // and try again } |
As you can see, we’re using a while loop to continuously loop until the user provides valid input. If they don’t, we ask them to try again until they either give us valid input, shutdown the program, or destroy their computer.
Error case 2: Extraction succeeds but with extraneous input
Consider the following execution of the above program:
Enter a double value: 5*7
What do you think happens next?
Enter a double value: 5*7 Enter one of the following: +, -, *, or /: Enter a double value: 5 * 7 is 35
The program prints the right answer, but the formatting is all messed up. Let’s take a closer look at why.
When the user enters “5*7” as input, that input goes into the buffer. Then operator>> extracts the 5 to variable x, leaving “*7\n” in the buffer. Next, the program prints “Enter one of the following: +, -, *, or /:”. However, when the extraction operator was called, it sees “*7\n” waiting in the buffer to be extracted, so it uses that instead of asking the user for more input. Consequently, it extracts the ‘*’ character, leaving “7\n” in the buffer.
After asking the user to enter another double value, the “7” in the buffer gets extracted without asking the user. Since the user never had an opportunity to enter additional data and hit enter (causing a newline), the output prompts all get run together on the same line, even though the output is correct.
Although the above problem works, the execution is messy. It would be better if any extraneous characters entered were simply ignored. Fortunately, that’s easy to do:
1 |
std::cin.ignore(32767, '\n'); // clear (up to 32767) characters out of the buffer until a '\n' character is removed |
Since the last character the user entered must be a ‘\n’, we can tell std::cin to ignore buffered characters until it finds a newline character (which is removed as well).
Let’s update our getDouble() function to ignore any extraneous input:
1 2 3 4 5 6 7 8 |
double getDouble() { std::cout << "Enter a double value: "; double x; std::cin >> x; std::cin.ignore(32767, '\n'); // clear (up to 32767) characters out of the buffer until a '\n' character is removed return x; } |
Now our program will work as expected, even if we enter “5*7” for the first input -- the 5 will be extracted, and the rest of the characters will be removed from the input buffer. Since the input buffer is now empty, the user will be properly asked for input the next time an extraction operation is performed!
Error case 3: Extraction fails
Now consider the following execution of the calculator program:
Enter a double value: a
You shouldn’t be surprised that the program doesn’t perform as expected, but how it fails is interesting:
Enter a double value: a Enter one of the following: +, -, *, or /: Enter a double value:
and the program suddenly ends.
This looks pretty similar to the extraneous input case, but it’s a little different. Let’s take a closer look.
When the user enters ‘a’, that character is placed in the buffer. Then operator>> tries to extract ‘a’ to variable x, which is of type double. Since ‘a’ can’t be converted to a double, operator>> can’t do the extraction. Two things happen at this point: ‘a’ is left in the buffer, and std::cin goes into “failure mode”.
Once in ‘failure mode’, future requests for input extraction will silently fail. Thus in our calculator program, the output prompts still print, but any requests for further extraction are ignored. The program simply runs to the end and then terminates (without printing a result, because we never read in a valid mathematical operation).
Fortunately, we can detect whether an extraction has failed and fix it:
1 2 3 4 5 6 |
if (std::cin.fail()) // has a previous extraction failed? { // yep, so let's handle the failure std::cin.clear(); // put us back in 'normal' operation mode std::cin.ignore(32767,'\n'); // and remove the bad input } |
That’s it!
Let’s integrate that into our getDouble() function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
double getDouble() { while (true) // Loop until user enters a valid input { std::cout << "Enter a double value: "; double x; std::cin >> x; if (std::cin.fail()) // has a previous extraction failed? { // yep, so let's handle the failure std::cin.clear(); // put us back in 'normal' operation mode std::cin.ignore(32767,'\n'); // and remove the bad input } else // else our extraction succeeded { std::cin.ignore(32767, '\n'); // clear (up to 32767) characters out of the buffer until a '\n' character is removed return x; // so return the value we extracted } } } |
Note: Prior to C++11, a failed extraction would not modify the variable being extracted to. This means that if a variable was uninitialized, it would stay uninitialized in the failed extraction case. However, as of C++11, a failed extraction due to invalid input will cause the variable to be zero-initialized. Zero initialization means the variable is set to 0, 0.0, “”, or whatever value 0 converts to for that type.
Error case 4: Extraction succeeds but the user overflows a numeric value
Consider the following simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <cstdint> #include <iostream> int main() { std::int16_t x { 0 }; // x is 16 bits, holds from -32768 to 32767 std::cout << "Enter a number between -32768 and 32767: "; std::cin >> x; std::int16_t y { 0 }; // y is 16 bits, holds from -32768 to 32767 std::cout << "Enter another number between -32768 and 32767: "; std::cin >> y; std::cout << "The sum is: " << x + y << '\n'; return 0; } |
What happens if the user enters a number that is too large (e.g. 40000)?
Enter a number between -32768 and 32767: 40000 Enter another number between -32768 and 32767: The sum is: 32767
In the above case, std::cin goes immediately into “failure mode”, but also assigns the closest in-range value to the variable. Consequently, x is left with the assigned value of 32767. Additional inputs are skipped, leaving y with the initialized value of 0. We can handle this kind of error in the same way as a failed extraction.
Note: Prior to C++11, a failed extraction would not modify the variable being extracted to. This means that if a variable was uninitialized, it would stay uninitialized in the failed extraction case. However, as of C++11, an out-of-range failed extraction will cause the variable to be set to the closest in-range value.
Putting it all together
Here’s our example calculator with full error checking:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#include <iostream> double getDouble() { while (true) // Loop until user enters a valid input { std::cout << "Enter a double value: "; double x; std::cin >> x; // Check for failed extraction if (std::cin.fail()) // has a previous extraction failed? { // yep, so let's handle the failure std::cin.clear(); // put us back in 'normal' operation mode std::cin.ignore(32767,'\n'); // and remove the bad input std::cout << "Oops, that input is invalid. Please try again.\n"; } else { std::cin.ignore(32767,'\n'); // remove any extraneous input // the user can't enter a meaningless double value, so we don't need to worry about validating that return x; } } } char getOperator() { while (true) // Loop until user enters a valid input { std::cout << "Enter one of the following: +, -, *, or /: "; char op; std::cin >> op; // Chars can accept any single input character, so no need to check for an invalid extraction here std::cin.ignore(32767,'\n'); // remove any extraneous input // Check whether the user entered meaningful input if (op == '+' || op == '-' || op == '*' || op == '/') return op; // return it to the caller else // otherwise tell the user what went wrong std::cout << "Oops, that input is invalid. Please try again.\n"; } // and try again } void printResult(double x, char op, double y) { if (op == '+') std::cout << x << " + " << y << " is " << x + y << '\n'; else if (op == '-') std::cout << x << " - " << y << " is " << x - y << '\n'; else if (op == '*') std::cout << x << " * " << y << " is " << x * y << '\n'; else if (op == '/') std::cout << x << " / " << y << " is " << x / y << '\n'; else // Being robust means handling unexpected parameters as well, even though getOperator() guarantees op is valid in this particular program std::cout << "Something went wrong: printResult() got an invalid operator."; } int main() { double x = getDouble(); char op = getOperator(); double y = getDouble(); printResult(x, op, y); return 0; } |
Conclusion
As you write your programs, consider how users will misuse your program, especially around text input. For each point of text input, consider:
- Could extraction fail?
- Could the user enter more input than expected?
- Could the user enter meaningless input?
- Could the user overflow an input?
You can use if statements and boolean logic to test whether input is expected and meaningful.
The following code will test for and fix failed extractions or overflow:
1 2 3 4 5 6 |
if (std::cin.fail()) // has a previous extraction failed or overflowed? { // yep, so let's handle the failure std::cin.clear(); // put us back in 'normal' operation mode std::cin.ignore(32767,'\n'); // and remove the bad input } |
The following will also clear any extraneous input:
1 |
std::cin.ignore(32767,'\n'); // and remove the bad input |
Finally, use loops to ask the user to re-enter input if the original input was invalid.
Note: Input validation is important and useful, but it also tends to make examples more complicated and harder to follow. Accordingly, in future lessons, we will generally not do any kind of input validation unless it’s relevant to something we’re trying to teach.
![]() |
![]() |
![]() |
Hello! Just a quick note, I think in Error Case 3 in the code section under where you say "Let’s integrate that into our getDouble() function:", it's missing another std::cin.ignore(32767,'\n'); statement in between lines 15 and 16. Looks like it's back in there in the final example.
Fixed. Thanks for pointing out the omission!
Hi your tutorials have been very helpful.Thanks for the good c++ tutorials.I would like if someone could look at my code for the calculator and say what they think and feedback is much appreciated.
calculator.cpp
calculator.hpp
calculator.cpp
Hi Faruk!
* Initialize your variables with uniform initialization
* Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max()
* Misleading indention. Line 40ff is unreachable. Use curly brackets and the auto-formatting feature of your editor.
* Use ++prefix unless you need postfix++
Thanks for the advice.
Hi Alex,
i believe that there is a linguistic mistake in this chapter as following the explanation of the extraction operator ">>" you have put
"Extraction succeeds if at least one character can be extracted from the input buffer. Any unextracted input is left in the input buffer for future extractions. For example:"
now this is a bit misleading as the term at least would imply that if i had declared an integer variable then the user input "abc1" while the characters abc cannot be extracted and assigned to an integer variable the "1" can.
I have just quickly tested this with std::cin on my compiler and it doesnt seem to pick up the 1 but silently fail, so i presume that it is the linguistics here that are incorrect (or do not make it clear at least to myself a native english speaker with a degree in english language so im sure im not alone!)
loving the tutorials so far :)
I changed "can be" to "is", since in the case of "abc1" as input for an integer, no characters are extracted. Hopefully that's enough to clarify.
I'm on c++ 11 or above.
When running the program Alex has mentioned in Error Case 4,
I get the following output (which is dissimilar to Alex's):
OUTPUT1
Enter a number between -32768 and 32767: 32769
Enter another number between -32768 and 32767: The sum is: 32767
Program ended with exit code: 0
Please help to understand this behavior.
When I edit the program to:
[code]
int main()
{
std::int8_t x { 0 }; // x is 16 bits, holds from -32768 to 32767
std::cout << "Enter a number between -128 and 127: ";
std::cin >> x;
std::int16_t y { 0 }; // y is 16 bits, holds from -32768 to 32767
std::cout << "Enter another number between -32768 and 32767: ";
std::cin >> y;
std::cout << "The sum is: " << x + y << '\n';
return 0;
}
I get the following output:
OUTPUT2
Enter a number between -128 and 127: 130
Enter another number between -32768 and 32767: The sum is: 79
Program ended with exit code: 0
OUTPUT1 and OUTPUT2 seem to be having different kind of logic.
In OUTPUT1, variable x was assigned the maximum value that could be assigned to an int16_t variable when cin extracted an "overflow" value.
But the same is not seen in OUTPUT2.
Shouldn't this be consistent irrespective of the variable type?
1) It looks like the way std::cin and operator<< handle invalid inputs changed in C++11. Now instead of leaving the variable alone, it always initializes it with some value. I've updated the lesson text.
2) int8_t is typically a typedef for char, and chars tend to have different handling than int. Avoid int8_t.
For the above program, if the input given after "Enter a number" is anything between "5a" to "5f" the output is as shown below :
Enter a number
5f
Enter a character
0
a
Program ended with exit code: 0
But if the input given after "Enter a number" is anything more than "5f" like "5g" or "5h" the output is correct as shown below:
Enter a number
5g
Enter a character
5
g
Program ended with exit code: 0
I'm not sure why this is so. NOTE: the issue is seen for "5i" too.
I have this code :
I get this output:
Enter a number
d5
Enter an alphabet
0
a
Program ended with exit code: 0
Question:
Even if I have initialized variable "integer" to "1". When I enter "d5" as the input for the first cin, how does "0" get assigned to variable "integer"?
I now understand that a failed extraction leads to "zero-initialization" which is zero assignment in this case, due to which I get the output posted in my previous comment.
When using either while(1) or while(true), I get a warning that the condition is constant. Obviously because by design the loop is supposed to be infinite for this sort of thing.. but my compiler(VC2013) isn't having any of that. How to get around this?
Hi Rasikko!
is valid and commonly used. Warnings can be disabled in VS, see https://docs.microsoft.com/en-us/visualstudio/ide/how-to-suppress-compiler-warnings?view=vs-2017
Thanks for the link. I learned of a pragma function that allows me to disable specific warnings.
Hi Alex,
In section 'Error case 1: Extraction succeeds but input is meaningless':
"2) If so, return the value to the user."
Did you mean to say:
"2) If so, return the value to the caller."?
Yup. Fixed. Thanks!
Do these statements change the range of values the variable can store.
int16_t a;
int8_t b;
int32_t c;
It is good if they do. I have not tried these declarations before
Yes they do. The number after "int" is the number of bits the type can store.
@std::int8_t can store 2^8 values
@std::int16_t can store 2^16 values
@std::int32_t can store 2^32 values
Note however, that these types aren't guaranteed to be implemented by the compiler. See the "Types" list over here for alternatives http://www.cplusplus.com/reference/cstdint/
Thanks a lot
Selam Dear Mr Alex/Nas
Depending on the quiz at section 6.9a I was trying to write a full code that fulfills all the lows and dealing with invalid text input. As you see the below code i expected to have an out put that loops the question again and again if the input is invalid or fails. and print the how much names i am gonna register if not fails, however what i have seen is different from my expectation.To save your time i don't write the out put c/s you already know how it outputs. So Please tell where my mistake was started? It takes me a lot of time to fix and i cant so far.
God bless you both!!!!!!!!
Hi!
* Don't use "using namespace"
* Initialize your variables with uniform initialization
* Line 13 should be moved behind into an else block, because it won't work if extraction failed.
* Line 21: @std::cin.fail() cannot return true here, because you cleared all errors beforehand
Dear Mr. Nas!
Thank you so much!
I have learnt more from your answers. It works with do{} while(!length);
Stay blessed Sir.
This reads weird:
When we use operator>> to get user input and put it into a variable, this is called an “extraction”. operator>> is accordingly called the extraction operator when used in this context.
Consider:
When we use the >> operator to get user input and put it into a variable, this is called an “extraction”. The >> operator is accordingly called the extraction operator when used in this context.
Fixed. Thanks!
Hi Alex,
Just a minor fix:
The indent of the curly bracket in line 14 of the function under "Types of invalid text input - Error case 1: Extraction succeeds but input is meaningless" and line 46 of the code under "Putting it all together" should be one tab less. :)
Fixed! Thanks!
Dear Mr Alex/Nas
My code below is working to me very nicely since i entered '+' operator (NOTE:-I did that for demo purpose only)
But please i need you to revise my usage for while statement and waiting for your regretful comment if there is a batter or shortest way than mine. Please focus only on a while statement. (to save your time)
stay bless more and more!!!!!!!!!!!!
Hi!
There's nothing wrong with your loop.
* Line 7, 26, 30: Initialize your variables with uniform initialization
* Line 13, 18, 28, 32: Use @std::numeric_limits<std::streamsize>::max(). See the documentation of @std::basic_istream::ignore
* Line 19: Double line feed
* Line 38: Don't use @system. If you want to pause the program for whatever reason, use @std::cin.get
Dear Mr.Nas!
I have no words for you both! I really blessed to have you!
I have taken all your great comments. I am enjoying programming b/s of you!!!!
Always my prayer great God to be with you!!!!
Dear Mr Alex/NAS!
Kindly and very thank fully ,This is my last question for this section.
In the above example ("putting it all together") in the 1st function double get Double() What was the reason for declaring std::cin.ignore(32767,'\n'); after "else" (line 21). B/s i think it increases the number of errors and "try again" statements by one.
For example in the current function definition above,if the user enters double number 3.0 but if there was extraneous input at the buffer (Eg. '\n') the input fails and the function asks the user to input again.
But if we declare std::cin.ignore(32767,'\n');as an input validation after the cin>>x; (line ten), it deletes extraneous input ('\n') and pass the user's double value to be extracted that enables the if(std::cin.fail) statement to be false and execute what we want at the 1st time the user inputs.
Would you explain more on what was preferable cin.ignore() usage from using it after else(line 21) or after Cin>> extraction(line ten).
Thank you so much in advance for your usual and constant kindness my dears!!!!!!
@std::basic_istream::ignore cannot clear the stream when it's in a bad state.
If you called it on a bad stream before calling @std::basic_istream::clear it wouldn't do anything. But you don't want to call @std::basic_istream::clear on a stream that is in a good state. Alex' code is correct.
DEar Mr Nas!
I becoming to love questioning you b/s i have gotten so beloved answers whenever i asked you.
Always i am thank full for your perpetual kindness, Sir!
Great tutorial. Thank you so much for your time and effort! Excellent information.
In this particular article, I'm not sure I agree with the statement, "However, parsing strings and converting ... is only done in rare cases." It's probably true, but shouldn't be (necessarily). Playing the "semantics police" here... there are some of us who believe that if you ask for numeric input and the user enters non-digit characters, that should be flagged as a format error and cause a reprompt. For example:
This is "probably" a typo... should the value be 1245, 12345 (# = <shift>3), or some other value? Rather than allow 12 to be successfully extracted and "#45" discarded, the entire input should be rejected.
Checking for this was a simple task in straight 'C'... the 2nd parameter of strtol(3) (i.e. **endp) allows the caller to examine the character that caused the numeric conversion to terminate. If it isn't an ASCII NUL ('\0'), then it is a format error (assuming you've already stripped off trailing whitespace). Additionally, the 3rd parameter of strtol(3) (i.e. the radix) allows you to restrict the notion of a valid digit. For example, if I ask for a decimal value, I don't want to read something like 0x5D6. I merely set the radix to 10 and go from there.
[Note: Of course, when I say that it's a "simple" task, I'm ignoring the fact that you have to use fgets(3) to read in a line of input, strip off trailing whitespace, etc. Which is why I don't disagree when you say parsing strings can be challenging. :-)]
So... is there a simple way to accomplish these sorts of format checks in C++ "on the fly", without using getline() first and then parsing the string directly? I don't see how (consider the input string "123 456" for example, which has intermediate whitespace)... but then, I wouldn't be here if I was a C++ guru, right? :-)) Any insights appreciated.
Hi Bullwinkle!
@std::strtol and it's variants are still present in cpp. But they aren't required when dealing with streams, because those have built-in functionality for parsing.
> "#45" discarded
It's not discarded, it remains in the stream, allowing you to check for what you described using @std::istream::peek.
References
std::basic_istream::peek - http://www.cplusplus.com/reference/istream/basic_istream/peek/
I just wanted to ask, I usually see people use
for cin.ignore. Is that better, or is it the same as this?
Hi Arush!
What you mean is
Yes, that's not only better, but it's the only correct way. Passing this value to @std::cin.ignore causes it to skip all input until it hits the delimiter (Usually a line feed). Passing any other value will stop skipping characters after that value has been reached and there could potentially be more input in the stream, causing your program to malfunction.
Alex uses 32767, because it's easier, you should use
References
std::basic_istream::ignore - https://en.cppreference.com/w/cpp/io/basic_istream/ignore
When asking the user for input, the following code results in having to make an input at least twice.
Using the debugger it seems that the line
is causing the problem. After it is executed i have to make an input. Then i have to make an input again for the actual cin >> number.
Hi Donlod!
@std::cin.clear clears the error flag. There's no reason to call it when no error occurred.
@std::cin.ignore clears the input buffer. There's no reason to call it when there's no trailing data in the buffer.
Change these things and your problem is gone.
I can't find it in the documentation, but it seems as if calling functions that read from the input buffer when the input buffer is empty wait for input to be made.
Thanks for the reply.
So is it possible to do this the "clean way"?
I came up with two possible solutions:
But if i remember correctly, we should avoid using break, continue in loops, right? Also i think the code is more self-explanatory when actually having a clearer loop condition.
And this:
Here i do not like the fact to check twice for cin.fail().
> we should avoid using break, continue in loops, right?
I can't find a rule for this and I don't see anything wrong with using break/continue.
Your first solution is the better one, because, as you already said, you're checking twice in the second solution. There are three issues with it though
* You're using an int for the while-loop's condition
* You're using a magic number
* You have duplicate code
I don't think there's a nicer solution to this, but since this code is independent of the input extraction itself we can write a wrapper function for it so we don't need to repeat this all over the place.
Would seem better to rename @cleanStream to something like notCleanStream, considering you are returning true if extraction fails. If extraction fails you do not have a cleanStream. Either that or change the function to return false if the extraction failed so the while loop reads as
Thoughts?
It's the verb "clean", not the adjective.
Hi guys,
I am having an issue with extraction handling.
My issue is that it is not working.
this is a snip of what I am trying and it does not handle my failures. Anything I type, gets passed.
Hi Joe!
* Initialize your variables.
* Don't use goto.
Anything you can type can be converted to a char.
Letters? Sure
Numbers? Sure, a char is a number.
Special characters? Yep, those fit in a char
Strings? The first character can be extracted.
If you only want to allow a specific subset of characters you'll need to filter them manually. eg. for only allowing lower case:
Have a look at the ascii table to see the order of characters http://www.asciitable.com/
Thank you again nascardriver. I really appreciate the help and input.
Tell me, what is the item with which you initialized char c with? \x00
\x interprets the next two characters as a hexadecimal ascii code. So '\x00' is just 0, you could use 0, I use '\x00' when I use a char to hold a character and 0 when it holds a number, do what you like.
Nevermind, I figured it out. Thank you again.
How about this?
I know its clunky, but I have more to learn then I can cut it to better size.
actually, this works
Keep in mind that 32767 doesn't have any special effect to @std::cin.ignore. In a real program std::numeric_limits<std::streamsize>::max() should be used, this causes @std::cin.ignore to ignore everything up to the next newline.
Groovy,
again thank you nascardriver.
"Error Case 3", code line 15:
it wasn't clear to me why x was "good" after this failure.
Hi Peter!
Hi nascardriver! (Are you really a nascardriver?)
I think the problem for me was the comment wording and the way it was laid out. Perhaps the following will be clearer to some people:
I have a question. In this section we used cin.fail(), cin.clear(), and cin.ignore() functions, but where can I look to see what other similar associated functions that there might be for std::cin, and any other #include library commands, types or classes, and what they do and how they work? (Also, I'm just curious - when we use std::cin, we don't need to add .function() or parenthetical parameters, so how does it just "know" to take input using the >> operator? Is this just something built in to compilers to treat certain library things the same way they would a built-in operation?) Thank you in advance for an answer.
Hi WiseFool!
cppreference and cplusplus have good documentations and examples to many functions.
http://en.cppreference.com/w/cpp/io/basic_istream/ignore
http://www.cplusplus.com/reference/istream/istream/ignore/
A great question. It would be nice to include some of this information about documentation, as well as a little summary table of these functions/methods within the lesson.
How's this for error handling (and code writing i.e any bad habits or better way to do it)?
Hi Nick!
I had to include two additional header to allow proper use of NaN. The same could be achieved by defining an arbitrary value to be used as NaN.
Very minor nitpick with the ordering of the error handling explanations; the 3rd and 4th single line error descriptions match up to the 4th and 3rd explanations. I.e.:
"Input extraction succeeds but the user overflows a numeric value." - 3rd in list
"Input extraction fails (e.g. trying to enter ‘q’ into a numeric input)." - 4th in list
Then below:
"Error case 3: Extraction fails" - matches 4th in list above
(...some text...)
"Error case 4: Extraction succeeds but the user overflows a numeric value" - matches 3rd in list above
(...some text...)
Sorry if I haven't made this very clear, or if it's intentional.
Fixed, thanks!
Hi Alex,
why ignore up to 32767 characters? what's special about this number?
It's the largest 2-byte signed integer. We need a number larger than 1 just in case there are multiple characters to ignore. So what should we pick? 2? But what if there are 3 characters? 3? What if there are 4? So we pick the largest integer value that's guaranteed to fit in an integer.
when the user enters 5*7 , y does *7/n remains in buffer and not only *7..why has the new line character appeared in the buffer without user sending it in cin?
When you hit enter on your keyboard, that key is represented as a '\n' character in ASCII. Therefore, \n is in the buffer because you put it there. :)
What if I want to accept integers from the user until he enters space or presses enter twice, extraction doesn't fail in this case, how can I do it?
This is an odd request, and I'm not sure why you'd want to do this. But my first thought is to read things in character by character, and reconstruct the integers the user is entering from the number characters.
Hey, why do you use the number 32,767 exactly in cin.ignore() function. Does the number has to do anything with 'int' datatype?
Yeah, it's the maximum value for a 2-byte signed integer.
bit or byte?
Byte. My bad. Edited the previous comment for clarity.
We use Enter button to confirm our input. Is there any other ways to confirm our input and make our program go to next step (I am trying to confirm input without trailing '\n')?
Not that I'm aware of.