I'm trying to use safe practices in handling input with numbers only in C++, so I use a stringstream object as so:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
int first, second;
string input;
stringstream sstream;
cout << "First integer: ";
getline(cin, input);
sstream.str(input);
sstream >> first;
cout << first << endl; //display user input in integers
cout << "Second integer: ";
getline(cin, input);
sstream.str(input);
sstream >> second;
cout << second << endl; //display user input in integers
getline(cin, input); //pause program
return 0;
}
However, the second time around it seems to give the variable 'second' an arbitrary value. This is the output:
First integer: 1
1
Second integer: 2
2293592
If I declare two stringstream objects and use them respectively for both variables it seems to work fine. Does this mean that I cannot re-use a stringstream object in the way I'm trying to do? In my real program I intend to handle much more than two input values from the user, so I just want to make sure if there's another way instead of making multiple stringstream objects. I doubt it's of great relevance but I'm on Windows XP and I'm using MinGW as my compiler.
I greatly appreciate any help.
Use sstream.clear(); after sstream >> first;.
You need to reset the state of the stringstream. Generally, this involves two steps: clearing the buffer:
sstream.str("");
and resetting the error state flags:
sstream.clear();
If you don't clear the buffer, if you get an input like "123abc" then "abc" will still be in the stream when you try to read from it the next time.
You should also make sure to test the fail state of the stream (sstream.fail()) to ensure that the extraction was successful. If you want to be sure that the user only entered an integer (i.e., you want to prevent the user from inputting, say, "123abc", then you should test to make sure sstream.eof() is true.
A better way to do this conversion between datatypes would be to use boost::lexical_cast.
Info and examples can be found at the Boost Site.
Below is an example of doing an int to string conversion and back (string to int) such as what you're doing in your program.
#include <string>
#include <boost/lexcal_cast.hpp>
int main(int argc, char *argv[])
{
int i = 42;
std::string s = boost::lexical_cast<std::string>(i);
int j = boost::lexical_cast<int>(s);
return 1;
}
cout << "First integer: ";
getline(cin, input);
sstream.str(input);
sstream >> first; // state of sstream may be eof
cout << "Second integer: ";
getline(cin, input);
sstream.str(input);
sstream.clear(); // clear eof state
sstream >> second; // input from sstream
Related
In my case, I have to make sure the user input is either 1 or 2, or 3.
Here's my code:
#include <iostream>
using namespace std;
void invalid_choice_prompt() {
string msg = "\nInvalid Command! Please try again.";
cout << msg << endl;
}
int ask_user_rps_check_input(int user_choice) {
if (user_choice == 1 || user_choice == 2 || user_choice == 3) return 1;
return 0;
}
int ask_user_rps() {
// ask user's choice of Rock or Paper or Scissors
while (1) {
string msg =
"\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
cout << msg << endl;
int user_choice;
cin >> user_choice;
if (ask_user_rps_check_input(user_choice)) {
return user_choice;
}
invalid_choice_prompt();
}
}
int main() {
ask_user_rps();
return 0;
}
The code is capable to handle the situation when the input is an integer, but when the input are characters or strings, the program will be trapped in the infinite loop.
Is there any elegant way to do this? I've found some methods about using cin.ignore to ignore the specified length of io buffer, but I don't think this method is flexible enough. I am looking for a more flexible solution.
I think an option would be to collect the user input to a string and then move it to stringstream using getline kind of like this:
std::string input;
std::getline(std::cin, input);
//Now check if the input is correct. if it is, then:
std::stringstream stream;
stream << input;
int num;
stream >> num;
I'm not sure if this is a good method but it works.
One of the simplest solution would be to check the cin stream failure something like below:
int ask_user_rps() {
// ask user's choice of Rock or Paper or Scissors
while (1) {
string msg =
"\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
cout << msg << endl;
int user_choice;
cin >> user_choice;
if(cin.fail()) {
invalid_choice_prompt();
std::cin.clear();
std::cin.ignore(256,'\n');
continue;
}
if (ask_user_rps_check_input(user_choice)) {
return user_choice;
}
invalid_choice_prompt();
}
}
Reading from a stream using operator >> takes as many characters from the stream as the target type accepts; the rest will remain in the stream for subsequent reads. If the input has a format error (e.g. a leading alphabetical characters when an integer is expected), then an error-flag is set, too. This error-flag can be checked with cin.fail(). It remains set until it gets explicitly cleared. So if your code is...
int user_choice;
cin >> user_choice;
and if you then enter something that is not a number, e.g. asdf, then user_choice has an undefined value, an error-flag cin.fail() is (and reamins) set. So any subsequent read will fail, too.
To overcome this, you have to do three things:
First, check the error-flag. You can do this either through calling cin.fail() after a read attempt of through checking the return value of the expression (cin >> user_choice), which is the same as calling cin.fail().
Second, in case of an error, you need to clear the error-flag using cin.clear(). Otherwise, any attempt to read in anything afterwards will fail.
Third, if you want to continue with reading integral values, you need to take the invalid characters from the stream. Otherwise, you will read in asdf into a variable of type integer again and again, and it will fail again and again. You can use cin.ignore(numeric_limits<streamsize>::max(),'\n'); to take all characters until EOF or an end-of-line from the input buffer.
The complete code for reading an integral value with error-handling could look as follows:
int readNumber() {
int result;
while (!(cin >> result)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(),'\n');
cout << "Input is not a number." << std::endl;
}
return result;
}
Take input as char
string user_choice;
cin >> user_choice;
check input is valid or not if(user_choice=='1')
#include <iostream>
#include <string>
using namespace std;
int main()
{
int num;
cin >> num;
string s;
getline(cin, s);
cout << s << " " << num << endl;
return 0;
}
In this code if I input 3 and press enter, then s takes an empty string.
1) If it is taking the first character as a newline, then is there a possible solution of taking line as input after taking an integer as input?
2) If my input is 4567artyu then how it is deciding whether 7 has to go into the s or num ?
I recommend that you always read complete lines of input from your users. It will cause the least confusion.
Ask for input.
Use std::getline to read a line of input.
If you don't want a string but, say, an integer, use std::stoi or (more general) boost::lexical_cast to safely convert the input to your desired target type. This is where you catch poor inputs and complain at the user.
I don't think that many users, if prompted for a number, would expect that entering 42bananas would be accepted as 42 and the bananas part be “remembered” for later. It will most likely be a typo and the user will be happy to be asked to correct it.
For taking line as input after taking integer as input you can consider removing the stray '\n' character from the stream.
#include <iostream>
#include <string>
using namespace std;
int main()
{
int num;
cin >> num;
getchar();
string s;
getline(cin, s);
cout << s << " " << num << endl;
return 0;
}
This will do the trick.
For second question, it reads 4567 as integer, it will continue to read it as integer until limit of int is reached and if limit is reached it will not consider anything after that. Then it will put the maximum value of int in the variable num and null int the string s. If limit is not reached, then string will remain in the input stream as it is, and will be fetched by variable s.
Try using cin.clear before you accept string
I am creating this revese string App but i get a error if i include a space in the string !
#include <iostream>
#include <string>
using namespace std;
int main()
{
int inputa;
cout<<"%%%%%%%%%%%%%%%%%%String Reversing App%%%%%%%%%%%%%%%%%%%%%%%%"<<endl<<endl;
cout<<"\nEnter 1 to continue and 0 to exit"<<endl<<endl;
cin>>inputa;
if(inputa!=0)
{
do
{
string a,c="";
cout<<"\nEnter the string you want to Reverse : ";
cin>>a;
for(int x=a.length()-1; x>=0; x--)
{
c=c+a.substr(x,1);
}
cout<<"\nThe Reverse String is : "<<c<<endl;
cout<<"\nEnter 1 to continue and 0 to exit"<<endl<<endl;
cin>>inputa;
}
while(inputa!=0);
}
//not my home work
}
If I type the following string like "abc def" there i get an error . But otherwise it works perfectly ! Is there some mistake with the codes ! I am new to CPP so it would be helpful if you could help me !
operator>> will stop reading at the first space (as David pointed out) - use getline instead
std::string a;
getline(std::cin, a);
Full edit of your code
#include <iostream>
#include <string>
#include <limits>
int main()
{
std::cout << "%%%%%%%%%%%%%%%%%%String Reversing App%%%%%%%%%%%%%%%%%%%%%%%%\n\n";
std::cout << "\nEnter 1 to continue and 0 to exit" << std::endl;
int inputa;
std::cin >> inputa;
if(std::cin && inputa!=0)
{
std::cin.ignore(std::numeric_limits<int>::max( ), '\n');
do
{
std::string a,c;
std::cout<<"\nEnter the string you want to Reverse : ";
getline(std::cin, a);
for(int x=a.length()-1; x>=0; --x)
{
c+=a[x];
}
std::cout<<"\nThe Reverse String is : " << c << std::endl;
std::cout << "\nEnter 1 to continue and 0 to exit" << std::endl << std::endl;
std::cin >> inputa;
std::cin.ignore(std::numeric_limits<int>::max( ), '\n');
}
while(std::cin && inputa!=0);
}
}
Including David's verbatim answer because he answered with much more detail (David Rodríguez - dribeas) - please +1 him before he deletes it. His answer adds much more information that I did not mention so we are merging this into a single reply at Davids request,
The answer by Adrian is correct, deals with the immediate issue and provides a solution. As to why it enters an infinite loop, the reason is that after reading the first word, you are trying to read an integer std::cin >> inputa, which will fail as cde cannot be parsed as an integer. At this point the stream enters a fail state and subsequent reads will fail without doing anything (until you clear the error state).
What should you do?
If you want to process whole lines, then you should use std::getline, rather than operator>>. Beware on mixing both, as operator>> won't consume the spaces after the read (including new lines) and you might just read an empty line with the next std::getline. You can either always read with std::getline and then parse the line, or use ignore to clear up to the newline. Finally, whenever you perform IO operations, don't expect the operation to succeed: check the state of the stream. If you don't and your loop depends on IO to complete, it is quite easy to enter this sort of infinite loop, where the stream is marked as failed, no later reads succeed and you never break out of the loop.
This question already has answers here:
Need help with getline() [duplicate]
(7 answers)
Closed 7 years ago.
This is probably a very simple problem but forgive me as I am new.
Here is my code:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main ()
{
string name;
int i;
string mystr;
float price = 0;
cout << "Hello World!" << endl;
cout << "What is your name? ";
cin >> name;
cout << "Hello " << name << endl;
cout << "How old are you? ";
cin >> i;
cout << "Wow " << i << endl;
cout << "How much is that jacket? ";
getline (cin,mystr);
stringstream(mystr) >> price;
cout << price << endl;
system("pause");
return 0;
}
The problem is that when asked how much is that jacket? getline does not ask the user for input and just inputs the initial value of "0". Why is this?
You have to be careful when mixing operator>> with getline. The problem is, when you use operator>>, the user enters their data, then presses the enter key, which puts a newline character into the input buffer. Since operator>> is whitespace delimited, the newline character is not put into the variable, and it stays in the input buffer. Then, when you call getline, a newline character is the only thing it's looking for. Since that's the first thing in the buffer, it finds what it's looking for right away, and never needs to prompt the user.
Fix:
If you're going to call getline after you use operator>>, call ignore in between, or do something else to get rid of that newline character, perhaps a dummy call to getline.
Another option, and this is along the lines of what Martin was talking about, is to not use operator>> at all, and only use getline, then convert your strings to whatever datatype you need. This has a side effect of making your code more safe and robust. I would first write a function like this:
int getInt(std::istream & is)
{
std::string input;
std::getline(is,input);
// C++11 version
return stoi(input); // throws on failure
// C++98 version
/*
std::istringstream iss(input);
int i;
if (!(iss >> i)) {
// handle error somehow
}
return i;
*/
}
You can create a similar function for floats, doubles and other things. Then when you need in int, instead of this:
cin >> i;
You do this:
i = getInt(cin);
Its because you have a '\n' left lying on the input stream from a previous call.
cin >> i; // This reads the number but the '\n' you hit after the number
// is still on the input.
The easiest way to do interactive user input is to make sure each line is processed independently (as the user will hit enter after each prompt).
As a result always read a line, then process the line (until you get familiar with the streams).
std::string line;
std::getline(std::cin, line);
std::stringstream linestream(line);
// Now processes linestream.
std::string garbage;
lienstream >> i >> garbage; // You may want to check for garbage after the number.
if (!garbage.empty())
{
std::cout << "Error\n";
}
Ignore some characters until line feed is reached.
cin.ignore(256, '\n')
getline (cin,mystr);
I'd like to know how to limit an input value to signed decimals using std::cin.
double i;
//Reading the value
cin >> i;
//Numeric input validation
if(!cin.eof())
{
peeked = cin.peek();
if(peeked == 10 && cin.good())
{
//Good!
count << "i is a decimal";
}
else
{
count << "i is not a decimal";
cin.clear();
cin >> discard;
}
}
This also gives an error message with the input -1a2.0 avoiding the assignation of just -1 to i.
If the backing variable of the cin is a number, and the string provided is not a number, the return value is false, so you need a loop:
int someVal;
while(!(cin >> someVal)) {
cin.reset();
cout << "Invalid value, try again.";
}
Combining the techniques from the top answer here and this website, I get
input.h
#include <ios> // Provides ios_base::failure
#include <iostream> // Provides cin
template <typename T>
T getValidatedInput()
{
// Get input of type T
T result;
cin >> result;
// Check if the failbit has been set, meaning the beginning of the input
// was not type T. Also make sure the result is the only thing in the input
// stream, otherwise things like 2b would be a valid int.
if (cin.fail() || cin.get() != '\n')
{
// Set the error state flag back to goodbit. If you need to get the input
// again (e.g. this is in a while loop), this is essential. Otherwise, the
// failbit will stay set.
cin.clear();
// Clear the input stream using and empty while loop.
while (cin.get() != '\n')
;
// Throw an exception. Allows the caller to handle it any way you see fit
// (exit, ask for input again, etc.)
throw ios_base::failure("Invalid input.");
}
return result;
}
Usage
inputtest.cpp
#include <cstdlib> // Provides EXIT_SUCCESS
#include <iostream> // Provides cout, cerr, endl
#include "input.h" // Provides getValidatedInput<T>()
int main()
{
using namespace std;
int input;
while (true)
{
cout << "Enter an integer: ";
try
{
input = getValidatedInput<int>();
}
catch (exception e)
{
cerr << e.what() << endl;
continue;
}
break;
}
cout << "You entered: " << input << endl;
return EXIT_SUCCESS;
}
Sample run
Enter an integer: a
Invalid input.
Enter an integer: 2b
Invalid input.
Enter an integer: 3
You entered: 3.
cin's >> operator works by reading one character at a time until it hits whitespace. That will slurp the whole string -1a2.0, which is obviously not a number so the operation fails. It looks like you actually have three fields there, -1, a, and 2.0. If you separate the data by whitespace, cin will be able to read each one without problem. Just remember to read a char for the second field.
I tried many techniques for reading integer input from the user using the >> operator, but in a way or another all my experiments have failed.
Now I think that getline() function (not the method with the same name on std::istream) and the strtol() function from the include cstdlib is the only predictable consistent solution for this problem. I would appreciate if someone proved me wrong. Here is something like the one I use:
#include <iostream>
#include <cstdlib>
// #arg prompt The question to ask. Will be used again on failure.
int GetInt(const char* prompt = "? ")
{
using namespace std; // *1
while(true)
{
cout << prompt;
string s;
getline(cin,s);
char *endp = 0;
int ret = strtol(s.c_str(),&endp,10);
if(endp!=s.c_str() && !*endp)
return ret;
}
}
*1: Placing using namespace whatever; to the global scope may lead to broken "unity builds" (google!) on larger projects, so should be avoided. Practice to not use that way, even on smaller projects!
Reading integers from files is a very different matter. Raúl Roa's approach can be good for that if properly worked out. I also suggest that wrong input files should not be tolerated, but it really depends on the application.
Be warned that using >> and getline() in the same program on cin will lead to some problems. Use one of them only, or google to know how to handle the issue (not too hard).
Something like:
double a;
cin >> a;
Should read your signed "decimal" fine.
You'll need a loop and some code to make sure it handles invalid input in a sensible way.
Good luck!