Under windows10 and VS2017:
I was trying to read a double number 1.1 from keyboard using istream and put it into a int type variable, say temp. In reason temp is 1 but the istream seems to be stuck in some error status. In expectancy istream should stop and wait for keyboard input but it continues another round read-from-buffer and error occurs this time.
I had checked the rdstate() and it was equal to 2 after the 2nd round read-from-buffer. I know it was abnormal but why?
To replicate, run the code, type 1.1 in console and hit enter, the error will show up.
Actually, I used int32 to try to store double for some reasons. The program is supposed to print valid input from keyboard. Here valid refers to that the input should not exceed the range of int32 or be double/readable character. Otherwise the program should print Invalid input on the screen.
#include <iostream>
std::istream& f(std::istream &in) {
int temp = 0;
while(true) {
while (in >> temp) {
if (temp == -1) {
break;
}
std::cout << temp << std::endl;
}
if (in.eof()|| temp == -1) break;
if (!in) {
std::cout << "Invalid input" << std::endl;
in.clear();
in.ignore(10000,32);
}
}
in.seekg(0, std::ios::beg);
return in;
}
int main(){
std::cout << "Please input some integers and end with ^Z or -1" << std::endl;
f(std::cin);
return 0;
}
Keep in mind that when you read 1.1 from the keyboard you're reading text. The program looks at that text and decides what value it represents, depending on the type of the variable that you're reading into. If you're reading into an int, the input routine reads the first '1', then sees '.', which can't be part of the text representation of an int, and it stops reading. Your variable gets the value 1. If you try to read another int from the same input stream, that '.' will stop the read immediately, since it can't be part of an int, and the attempted input fails.
Short answer: don't do that. If your input text looks like floating-point, read it as floating-point.
Try this:
#include <iostream>
std::istream& f(std::istream &in) {
std::string temp = "";
while(true) {
while (in >> temp) {
if (temp == "-1") {
break;
}
std::cout << temp << std::endl;
}
if (in.eof()|| temp == "-1") break;
if (!in) {
std::cout << "Invalid input" << std::endl;
in.clear();
in.ignore(10000,32);
}
}
in.seekg(0, std::ios::beg);
return in;
}
int main(){
std::cout << "Please input some integers and end with ^Z or -1" << std::endl;
f(std::cin);
return 0;
}
You are parsing character by character from the buffer. You cannot put a character into an integer. You are assuming you are reading 1.1 from the stream, but you're instead reading 1,.,1 from the buffer, and the . is throwing the error. The above portion works as you are reading characters and keeping them in a string.
Related
Made something like this:
int main()
{
while (true)
{
std::cout << "Enter a number between one and nine. \n";
int oneandnine;
std::cin >> oneandnine;
if (std::cin.fail())
{
std::cin.clear();
std::cin.ignore(100, '\n');
std::cout << "INVALID UNPUT!\n";
}else if (oneandnine <= 9 && oneandnine >= 1)
{
break;
}else
{
std::cout << "INVALID UNPUT!\n";
}
}
return 0;
}
and when input is provided something like this 456aihdb, getting something like this:
INVALID UNPUT!
Enter a number between one and nine.
INVALID UNPUT!
Enter a number between one and nine.
Why does it loop twice like this? is it because when the 456 is discarded and the rest aihdb isn't which causes it to loop again and skip a cin input?
It is exactly as you think it is.
The fail flag isn't set immediately, instead the formatted input operator reads the integer 456 into oneandnine, but doesn't set the fail flag since it's a valid integer value. That leads to the else case executing since std::cin.fail() is false and oneandnine is not between 1 and 9.
The next iteration you read the invalid input and the fail flag will be set leading to the second error output.
One common way to handle validation is to read the whole line into a string, put that string into an std::istringstream and use that to attempt to parse the input:
if (!std::getline(std::cin, line))
{
// Failure of some kind, could be EOF or something else
// Probably best not to continue in this case
}
std::istringstream iss(line);
if (!(iss >> oneandnine))
{
// Invalid input, report it as such
}
if (oneandnine < 1 || oneandnine > 9)
{
// Invalid number, report it as such
}
// Correct input, continue with program
Note that input such as 6abc will be considered valid by the above code. The 6 will be extracted into oneandnine and the abc part will silently be discarded. If that's not wanted there are other ways for the parsing (e.g. std::stoi or std::strtol if exceptions are not wanted). Do that instead of the >> extraction, but the rest of the code above should be fine.
std::istream's operator >> doesn't read in whole lines. It reads until it finds an invalid character or whitespace, if it has found a valid character before the invalid character the read operation succeeds and the invalid character is left in the stream.
In your example the first iteration successfully reads 456 and leaves aihdb in the stream. This fails your range check and the second iteration then tries to read the remaining characters which fails as the first character isn't a number.
If you want to read whole lines use std::getline then parse the whole line into a number. For example:
#include <iostream>
#include <string>
using std::cout;
int main()
{
while (true)
{
std::cout << "Enter a number between one and nine. \n";
std::string line;
std::getline(std::cin, line);
int oneandnine;
size_t pos;
try
{
oneandnine = std::stoi(line, &pos);
}
catch ( std::exception& )
{
oneandnine = -1;
}
if (pos != line.size() || oneandnine > 9 || oneandnine < 1)
{
std::cout << "INVALID INPUT!\n";
}
else
{
break;
}
}
return 0;
}
Here is a simple program that demonstrates this issue.
#include <iostream>
using namespace std;
double d;
int main() {
cin >> d;
if (cin.fail()) {
cout << "Input is invalid" << endl;
}
else {
cout << d << endl;
}
return (0);
}
Entering a value like adsfasdf or !##$!*##(&Adf prints Input is invalid as expected. Entering a numeric value like 13.52 returns 13.52, also as expected.
But entering something that starts out as a number, but follows with strings, such as 13.52asdfasdf!##$!##$!#A prints 13.52. So why does that kind of input not print Input is invalid?
How do I get this input to be treated as invalid, instead of just printing the numeric part?
In C++, when you try to read a value out of a stream, it will read as much as possible from the stream as long as it matches the expected format, leaving everything else untouched. If the read wasn't able to read any values at all, then it sets the fail bit on the stream. This explains why if you enter total garbage you get an error, whereas if you enter a number followed by garbage you'll read valid data, but still have garbage left in the stream.
One way to get around this is to use the std::getline function to read a line of text from cin, which will pick up everything the user typed in, and to then parse it by running it through a std::istringstream. Here's one way to do this, based on some code that we used to use at Stanford in our intro classes:
int readInt() {
while (true) {
std::string input;
std::getline(cin, input); // Read a line of text from the user
/* Set up an istringstream to read the data */
std::istringstream converter(input);
/* Try to read an int. If we succeed, make sure there wasn't
* any trailing data.
*/
int value;
if (converter >> value) {
char leftover;
if (converter >> leftover) {
std::cout << "Please enter an integer." << std::endl;
} else {
return value;
}
} else {
std::cout << "Please enter an integer." << std::endl;
}
}
}
You can easily customize / templatize this to read data of whatever type seems most appropriate.
I want this code to check whether the input is an int or not, and it works fine until I enter a float type number. I need a program that won't let through float numbers.
bool error;
int x;
string s;
do
{
cout <<"number: ";
cin >> x;
error=cin.fail();
if(error)
{
cout << "error" <<endl;
cin.clear();
}
getline(cin,s);
}while(error);
cout << x;
Read in all of the user's input line as a string, then convert the string to an integer with std::stoi.
Unfortunately std::stoi will happily stop converting when it hits the end of convertible characters, but it allows you to pass in a pointer to a position to be updated with the character that ended the conversion. If this position is not the end of the string, there was garbage on the line.
bool error = true; // assume user input is wrong
while (error)
{
if (std::getline(std::cin, s)) // grab the whole line
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length()) // converted all user input
{
error == false; // good data
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // converted user input is too big for int.
{
}
}
}
^
I recommend turning the input loop into a function. 1) It's easily reusable should you need to convert an int again. 2) It gets rid of some of the logic above because you can return when the input is tested and good.
int gimmieInt(std::istream& in) // Me eat input stream! Om nom nom nom!
{
std::string s;
int x;
while (true) // consider instead using a maximum number of retries.
// This increases complexity, so consider it *after* you have
// the basic version working
{
if (std::getline(in, s))
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length())
{
return x;
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // user input is too big for int.
{
}
}
}
}
std::istream& operator(std::istream&, int) will read a valid integer number up to any non matching character like '.', and no error state is set for the stream up to this point.
You better should read complete (whitespace separeated) chunks as std::string, and inspect them if these contain the desired format (e.g. using std::regex).
std::stoi() should also fail with an exception, if you're trying to convert the chunk.
I think, you are looking for something like this (C++11):
auto s = std::string{};
std::cin >> s;
if( std::all_of(std::begin(s), std::end(s),
[](char c) -> bool {
return c <= '0' && c <= '9';
}) ) {
std::cout << "you have entered an integer" << std::endl;
}
Somehow I thought, that the standard library contains a predicate that checks if a given char is a digit, but I could not find it now. Such a hypothetic is_digit() would allow make the code more readable:
if( std::all_of(std::begin(s), std::end(s), std::hypothetic::is_digit) ) {
std::cout << "you have entered an integer" << std::endl;
}
In my simple Fraction class, I have the following method to get user input for the numerator, which works fine for checking garbage input like garbage, but will not recognize user input which starts with an integer, and is followed by garbage, 1 garbage or 1garbage.
void Fraction::inputNumerator()
{
int inputNumerator;
// loop forever until the code hits a BREAK
while (true) {
std::cout << "Enter the numerator: ";
// attempt to get the int value from standard input
std::cin >> inputNumerator;
// check to see if the input stream read the input as a number
if (std::cin.good()) {
numerator = inputNumerator;
break;
} else {
// the input couldn't successfully be turned into a number, so the
// characters that were in the buffer that couldn't convert are
// still sitting there unprocessed. We can read them as a string
// and look for the "quit"
// clear the error status of the standard input so we can read
std::cin.clear();
std::string str;
std::cin >> str;
// Break out of the loop if we see the string 'quit'
if (str == "quit") {
std::cout << "Goodbye!" << std::endl;
exit(EXIT_SUCCESS);
}
// some other non-number string. give error followed by newline
std::cout << "Invalid input (type 'quit' to exit)" << std::endl;
}
}
}
I saw a few posts on using the getline method for this, but they didn't compile when I tried them, and I'm having trouble finding the original post, sorry.
Better check as follows:
// attempt to get the int value from standard input
if(std::cin >> inputNumerator)
{
numerator = inputNumerator;
break;
} else { // ...
Or yes: Follow recommendations to parse a complete input line combining std::getline() and std::istringstream appropriately.
I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}