I'm trying to write code that will give the user an error if they type anything besides a number, dont even know where to start
I'm trying to write code that will give the user an error if they type anything besides a number, dont even know where to start
Check it out - MSDN has something to this effect on the cin reference page.
Consider this post signed
I think, though I may well be wrong, that using getline and entering the number as a string, is the most robust method. You can then validate and convert the string to another format or data type as required.
Validating a string is something you definitely need to learn anyway.
Alternatively, here’s a nice little routine that forces the input of a number using a do loop until you enter a predetermined number of your choice, -1 in this example.
I found this example on 1. Keyboard Input and it is very well explained.
But I’m not suggesting that this is how it should be done.
Of course it depends on your application and what you’re trying to achieve, which will be different every time. So don’t get hung up on any particular solution.Code:float x = 0; string badChars; do { cout << "Enter a number (-1 = quit): "; if(!(cin >> x)) { cout << "The input stream broke!" << endl; cin.clear(); cin >> badChars; cout << "You typed \"" << badChars << "\" instead of a number." << endl; cout << "Please try again." << endl; } else if(x != -1) { cout << "You entered " << x << endl; } } while(x != -1); cout << "Quitting program." << endl;
Last edited by Michael432000; 09-18-2010 at 03:42 AM.
If you want some code that's ready to use, or if you wish to study an advanced sample, here is an example of how to do it safely:
Basically, read a string, use string streams to convert to proper type, check if the input data is valid, then return it. If any of the steps fails, ask for the input again.Code:#ifndef INPUT_20081018_H #define INPUT_20081018_H #include <boost/format.hpp> #include <iostream> #include <string> #include <sstream> namespace Stuff { namespace Input { template<typename StrT> void StreamOut(const StrT& Output) { std::wcout << Output; } void StreamOut(const char* Output) { std::cout << Output; } void StreamOut(const std::string& Output) { std::cout << Output; } template<typename T> void StreamOut(const boost::basic_format<T>& Output) { StreamOut(Output.str()); } template<typename StrT, typename T> bool StreamIn(StrT& Stream, T& Buffer) { std::wstring Answer; std::getline(Stream, Answer); std::wstringstream StrStream(Answer); void* SuccessPtr = (StrStream >> Buffer); return SuccessPtr != nullptr; } template<typename T> bool StreamIn(T& Buffer) { return StreamIn(std::wcin, Buffer); } // Read function overloads // Read function with no validator func template<typename DataT, typename StrT1> void ReadNoVal(DataT& Out, const StrT1& Question) { Read(Out, Question, L"Invalid input!\n", L"Invalid input!\n", [](const DataT&){ return true; }); } template<typename DataT, typename StrT1, typename StrT2> void ReadNoVal(DataT& Out, const StrT1& Question, const StrT2& ErrorType) { Read(Out, Question, ErrorType, L"Invalid input!\n", [](const DataT&){ return true; }); } template<typename DataT, typename StrT1, typename StrT2, typename StrT3> void ReadNoVal(DataT& Out, const StrT1& Question, const StrT2& ErrorType, const StrT3& ErrorValidation) { Read(Out, Question, ErrorType, ErrorType, [](const DataT&){ return true; }); } // Read functions with validator function template<typename DataT, typename StrT1, typename Validator> void Read(DataT& Out, const StrT1& Question, Validator ValidatorFnc) { Read(Out, Question, L"Invalid input!\n", L"Invalid input!\n", ValidatorFnc); } template<typename DataT, typename StrT1, typename StrT2, typename Validator> void Read(DataT& Out, const StrT1& Question, const StrT2& ErrorType, Validator ValidatorFnc) { Read(Out, Question, ErrorType, L"Invalid input!\n", ValidatorFnc); } template<typename DataT, typename StrT1, typename StrT2, typename StrT3, typename Validator> void Read(DataT& Out, const StrT1& Question, const StrT2& ErrorType, const StrT3& ErrorValidation, Validator ValidatorFnc) { for (;;) { StreamOut(Question); if (!StreamIn(Out)) { StreamOut(ErrorType); continue; } if (ValidatorFnc(Out)) break; StreamOut(ErrorValidation); } } } } #endif // INPUT_20081018_H
Good luck.
Ah, real code, that’s what I’m looking for. Really appreciate you posting this, thank you. Beautiful.
Wow, thanks both of you. I actually have a thing where I won't use any code until I know everything about it, so I'll be doing a lot of research on the code you guys used =) Thanks again! These will be a great reference for me
Alright, I'm trying to create my own version of the code you guys made. What I want to do is when the user inputs data into the win32 console and it's invalid (a letter), the letter is completely cleared out of the console, while keeping all other data on the screen. Then I'll put the blinking I bar (position to type) back where it was origonally.
Also #include <boost/format.hpp> is not in my library in Visual Studio 2010
Last edited by Ji33my; 09-21-2010 at 03:26 PM.
I don't think you can do it without using things "external" to the standard C++, like WinApi or ncurses/pdcurses. Why not just make the user repeat the input process?
Elysia, your code is slightly too advanced for me at the moment, but i will definitely be looking into it in the future when i get into that type of code.
I actually have an idea of how I want to do it. Once an error is posted, I'll develop code that will find the position of the inputed data within the console and cout spaces instead for each character in the string. That is more towards the end of my code....i'm actually struggling right now getting my program to work the way I want it. Here is the code and i'll explain what I'm trying to do
Code:#include "stdafx.h" #include <iostream> using namespace std; void error(int p) { char badChars; if(!(cin >> p)) { std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); cout << "The input stream broke!" << endl; cin.clear(p); cin >> badChars; cin.get(); } else cin >> p; } int _tmain(int argc, _TCHAR* argv[]) { int x = 0, b = 0, c = 0; cout << "Enter a (-1 = quit): "; error(b); cout << b; cout << "Enter c (-1 = quit): "; error(c); cout << c; return (0); }
Alright, clearly you can see what I'm trying to do. I want the possibility of invalid input computed outside my main function, and if theres no error it computes it and moves on to the next step in the main function. I'm struggling with getting this to work the way I want, not to mention the value the you type in does not transfer over to the variable in the main function which i can't figure out how to do.
ignore the -1 = quit portion, i just want to check for invalid input and then put the value into the specified variable in my main function
Last edited by Ji33my; 09-21-2010 at 04:01 PM.
You hardly see cin::clear called with an argument. That function is there to set a state for the stream, and by default, it is the good state. If you do call it with an argument, it's supposed to be a state variable. If this compiles, then I guess that means the state is based on int at some level. Anyway, that's not really pertinent knowledge for something like this.Code:void error(int p) { char badChars; if(!(cin >> p)) { std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); cout << "The input stream broke!" << endl; cin.clear(p); cin >> badChars; cin.get(); } else cin >> p; }
To fix this, lets try analyzing the flow of what you want.
And of course, you write error because functions calling functions are a good way of going "outside".I want the possibility of invalid input computed outside my main function,
You flip flopped steps. If we break it down even more, you ask the user for some data: std::cin >> input;and if theres no error it computes it and moves on to the next step in the main function.
which either was an integer or not: if (cin)
If it was an integer you can go on to the next step in main: return;
If it was not an integer, you need to reset the stream: std::cin.clear();
And clean up the users mistakes: std::cin.ignore(std::numeric_limits<streamsize>::m ax(),'\n');
And get back to this step: std::cin >> input;
Now, as you may have realized, you can't get to that last step without controlling what the program executes next. Normally the program proceeds in top down fashion:
work;
work;
work;
But you need to keep trying std::cin >> input; until the user tries something that the computer recognizes as an integer.
You need a loop, and a way for main to receive the integer.
The difference between this and Elysia's code is night and day. Elysia's code is multipurpose, and you can impose some sort of rule through additional verification. This is for strictly C++ ints, and anything that fits in them is valid. If that is not sufficient, (and why would it be?) then there is an additional test inside the loop condition, to keep trying until you get exactly the integer you want.Code:int getInteger(); int main() { int p = getInteger(); return 0; } int getInteger() { int p; std::cin >> p; while (!std::cin) { std::cin.clear(); std::cout << "The input stream broke! gj" << std::endl; std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); std::cin >> p; } return p; }
Last edited by whiteflags; 09-22-2010 at 03:30 PM.
That code is too advanced for me too, at the moment, but I will keep working on it, or towards it.
If that boost library is a dead dependency, then what about the reference to boost on line 16?
I managed to download c++ Boost libraries, and run a little tutorial, but it was killing my already slow machine, so I uninstalled it.
Can you copy those boost headers to somewhere else and just leave those that you want to use. A bit confusing for a beginner.
I would at least like to get that code compiled.
I hope you are successful soon, because I only rearranged statements you wrote in your attempt, for the most part.That code is too advanced for me too, at the moment, but I will keep working on it, or towards it.