PDA

View Full Version : input errors c++




lights
Dec 13, 2011, 02:19 PM
Hello MacRumors!

I'm a new member but a long time lurker, this forum is really great for just about everything.

I seem to be having a problem with my code for c++ using xcode.

Basically, the searchfunction returns a value of i which determines the location of the customer record in the stuct of arrays.

If the function finds the person, it will return a value of >= 0. Otherwise, it will return -1. I believe that works as intended, but what doesnt work is when I prompt the user for a deposit amount. Everything works fine, until a user inputs a letter or string.

Basically, my error checking is not working, my code goes into an infite loop.

Any help is appreciated.


i=searchfunction(bank, total);

if (i != -1 && i >= 0)
{
cout <<endl <<left <<setw(5) <<"" << "Customer Information found\n" <<endl;
cout <<left <<setw(5) <<"" << "name" << setw(24) <<right <<"" <<"Accunt Number" << setw(11) <<"" <<"balance" <<endl <<endl;
cout <<setw(5) <<"" <<setw(28) <<left <<fixed << bank[i].fullname;
cout <<setw(5) <<right << bank[i].acct<<setw(15) <<"$" <<setw(8) <<setprecision(2)<< bank[i].bal <<endl <<endl;

do{
cout <<endl <<left <<setw(5) <<"" << "Enter amount to deposit: $" ;
cin >> amount;
cin.ignore();

if (amount > 0)
{
bank[i].bal = (bank[i].bal + amount);
cout <<endl <<left <<setw(5) <<"" << "New Balance: $" << bank[i].bal <<endl;
updateInput(bank, total, fileName); // updates the customer file that was loaded in
backMenu(bank, size, total, target2, target, fileName); // back to main menu
}

else if(!cin)
{
cout <<endl <<left <<setw(5) <<"" << "Try again, Must be an integer number"<<endl;
cin.clear();
//backMenu(bank, size, total, target2, target, fileName); // back to main menu
}

else
{
cout <<endl <<setw(5) <<"" <<"Minimum Deposit has to be greater than $0\n"<<endl;
//backMenu(bank, size, total, target2, target, fileName); // back to main menu
}

}while(!cin || amount <= 0);

else
{ cout <<"customer not found" <<endl;
backMenu(bank, size, total, target2, target, fileName); // back to main menu



jiminaus
Dec 13, 2011, 03:09 PM
Nevermind. I missed the do {.

jared_kipe
Dec 13, 2011, 03:13 PM
I suggest trying to reproduce it in a much smaller sample program. Because there are so many <<'s in there its like a Where's Waldo for a misplaced <

Or that...

Sander
Dec 13, 2011, 03:53 PM
Nevermind. I missed the do {.

So did the original program. Reformatting it, it basically says:


if (something)
{
// some code
}
while (condition);


If "condition" happens to be true the first time through the if-body, the (empty) while-loop below will just keep iterating on its semicolon forever...

lights
Dec 13, 2011, 04:12 PM
Actually the if statement has the entire do while in there,

The else of the original if statement just says if the value is anything beside a value >=0.

I think your saying my program does if, then while?

I know, I just pasted my entire deposit function with the formatting or what not. Would it be easier if I rewrite it?

Thanks for the reply! I just want to fix this, I'm sure it's something simple I'm doing wrong. I just want it to do error checking for invalid input, like a user entering a letter instead of a number.




float amount=0;

i=searchnamefunction();

if (i >=0)
{
cout <<"customer found" <<endl;
do{
cout <<how much to deposit?"
cin >> amount;
con.ignore(10, '\n');

if(!cin)
cout <<"must be a number";

else if (amount <= 0)
cout <<"value must be greater than 0";

else
bank[i].bal = bank[i].bal - amount;

} while (!cin || amount <= 0);

}

else
cout <<"customer not found";


Sorry if there are syntax error, I rewrote it on my iPhone.

lloyddean
Dec 13, 2011, 05:41 PM
Would the user typing 12f be considered an error? Or would amount be 12 considered OK?

lights
Dec 13, 2011, 05:46 PM
12f would be considered an error!

But what I would want done first is if they only enter a letter or two, because it's driving me nuts lol

lloyddean
Dec 13, 2011, 05:55 PM
But what I would want done first is if they only enter a letter or two, because it's driving me nuts lol

Sorry I don't understand this part of your reply!

subsonix
Dec 13, 2011, 09:16 PM
It's the same horrible behavior that scanf() has, crap gets stuck in the buffer, enter infinite loop. The most robust way to validate input is to read into a string, then validate and convert to a numeric type.

How nice then that STL have a string class, but unfortunately it doesn't include a numeric conversion function. So use boost then, or go back and forth between string and c_string and use standard C library functions to convert to numeric types. It's not pretty.

strtof is rather nice, but it reads up until the first non-numeric character so 99kk would return 99. It updates it's end pointer argument though, so you can compare it to the start pointer + strlen() and if they don't match, reject.


float f;
string input;
char *endp;

do {
cout << "Enter a number: ";
cin >> input;
f = strtof(input.c_str(), &endp);
} while( endp != (input.c_str() + input.length()) );


cout << "The number was: " << f << endl;


Looking at you first code example this might become messy, so create your own input validation function or subclass existing STL classes to do what you want and test separately. Convert the number and if the input is rejected throw an exception. You can then validate all the conversion inside a try/catch block.

lights
Dec 13, 2011, 11:01 PM
@lloydean

What I mean is, I just want to do error check regardless if the user enters a letter or numbers followed by a letter.

@subsonix
I'm a bit confused, I think what your trying to have me do is a bit too advance, are you saying to create a function that checks the input of amount?

So I create a function with a pass by value of amount? How will I be able to convert a string to numeric?

All I want is an error check, so I thought either !cin or cin.fail() would work.

Im getting pretty desperate and don't know what else to try.

lloyddean
Dec 14, 2011, 01:00 AM
@lloydean

What I mean is, I just want to do error check regardless if the user enters a letter or numbers followed by a letter.

@subsonix
I'm a bit confused, I think what your trying to have me do is a bit too advance, are you saying to create a function that checks the input of amount?

So I create a function with a pass by value of amount? How will I be able to convert a string to numeric?

All I want is an error check, so I thought either !cin or cin.fail() would work.

Im getting pretty desperate and don't know what else to try.

Well from your comment "I think what your trying to have me do is a bit too advance"d I'm now thinking this might be homework. If it is the following possible solution will qualify as to advanced and you won't be able to turn it in. If not I think it will work although I haven't had the chance to test it much.



#include <iostream>
#include <string>
#include <sstream>

void getInput(const std::string& strPrompt, float& value)
{
int decimal_points;
bool invalid;
std::string str;
do {
std::cout << strPrompt << ": " << std::flush;
getline(std::cin, str);

// EXERCISE: trim any leading and trailing spaces within 'str'

invalid = false;
decimal_points = 0;

std::string::iterator itr = str.begin();
if ( ('-' == *itr) || ('+' == *itr) )
{
itr++;
}

for ( ; itr != str.end(); itr++ )
{
if ( !isdigit(*itr) )
{
if ( ('.' == *itr) && (++decimal_points <= 1) )
{
continue;
}

invalid = true;
break;
}
}
} while ( invalid );

std::istringstream iss(str);
iss >> value;
}

int main(int argc, const char* argv[])
{
std::string strPrompt("Give me a number");

float value;

while ( true )
{
getInput(strPrompt, strError, value);
if ( value >= 0.0f )
{
break;
}

std::cout << "Sorry, please provide a positive value!\n\n";
}

std::cout << "User provided value: " << value << std::endl;

return EXIT_SUCCESS;
}

lights
Dec 14, 2011, 01:39 AM
Yep, a little too advance lol, is there anything simpler? This is for CS1 final, just trying to cover all my bases for what the professor will input and I'm sure he'll enter a letter to check for input errors.

I'm using !cin for anything that goes into amount and when I debug it, the value of amount stays at zero. Even though I put 2p, urgh this error checking is getting really complicated :(

Sander
Dec 14, 2011, 03:12 AM
For an excellent summary, see here (http://www.cplusplus.com/forum/articles/6046/).

The problem is what subsonix said: There is stuff in the input buffer which is not numerical. When you use the >> operator to grab a number from the input stream, it will consume data up to the first non-numerical character. Even if that means not consuming anything at all, because the first character is already not numerical. Checking cin.fail() won't help, because this is not considered an error: The input stream is fine.

Parsing the numbers yourself (like in lloyddean's post) is an excellent exercise, but the link I posted above contains alternatives.

lights
Dec 14, 2011, 08:55 AM
For an excellent summary, see here (http://www.cplusplus.com/forum/articles/6046/).

The problem is what subsonix said: There is stuff in the input buffer which is not numerical. When you use the >> operator to grab a number from the input stream, it will consume data up to the first non-numerical character. Even if that means not consuming anything at all, because the first character is already not numerical. Checking cin.fail() won't help, because this is not considered an error: The input stream is fine.

Parsing the numbers yourself (like in lloyddean's post) is an excellent exercise, but the link I posted above contains alternatives.


You know what, I saw that a couple of days ago, but it was @ 4am and I forgot to bookmark it. It seems like a better way. Thank you!

I would try today, but I only have my iPad with me. Plus I have a clearsscreen function that is all over my code that Xcode doesn't like since it uses the windows.h header files.

subsonix
Dec 14, 2011, 09:18 AM
I'm a bit confused, I think what your trying to have me do is a bit too advance, are you saying to create a function that checks the input of amount?

So I create a function with a pass by value of amount? How will I be able to convert a string to numeric?

All I want is an error check, so I thought either !cin or cin.fail() would work.

Im getting pretty desperate and don't know what else to try.

The code snippet I posted does what you asked for, it reads in a string, then check to see if all characters where converted to a number. If not, then there where some non-numeric characters in there. You could use this as it is. My comment about creating a function or subclass STL string to add your own .to_string() function was only an idea of how you could refactor you code and separate input validation from your handling of bank accounts.

Here is an idea of how you could subclass STL string. You create a numeric_string class that inherits from STL string. It does exactly what I did in my previous post but throws an exception if it did not manage to convert all characters in the string. (strtof can also detect overflows btw, so you could potentially check for, and throw and exception for that as well)


#include <iostream>
#include <string>
#include <stdexcept>

class numeric_string : public std::string
{
private:

float f;
char *endp;

public:

float to_float() {
f = strtof(this->c_str(), &endp);
if( endp != (this->c_str() + this->length()) ) {
throw std::invalid_argument("Non numeric input");
}
else {
return f;
}
}
};


Further down we would now use our new numeric_string class, create an object called input and handle all inputs inside a try block. If we get the the bottom of all input, we set a 'valid_input' bool to true, if to_float() fails, it will throw an exception and we will end up in the catch block, an error messege is logged to stderr and 'valid_input' is set to false. Finally, we check if 'valid_input' is false in the loop condition. In this example I just read in three float variables, width, height, depth.


using namespace std;

int main()
{
float x, y, z;
numeric_string input;
bool valid_input;

do {

try {
cout << "Enter Width: ";
cin >> input;
x = input.to_float();

cout << "Enter Height: ";
cin >> input;
y = input.to_float();

cout << "Enter Depth: ";
cin >> input;
z = input.to_float();

valid_input = true;
}
catch(invalid_argument) {
cerr << "Only numbers!\n";
valid_input = false;
}

} while( valid_input == false );


cout << "Width: " << x << "\nHeight: " << y << "\nDepth: " << z << endl;


return 0;
}


Sander provided a link to how you can solve this by using getline() and convert with sstream instead, which is probably your best alternative.

S

lights
Dec 14, 2011, 04:23 PM
@subsonic. -- wow I would love to try your approarch to this, but sadly not enough time to finish before tomorrow. I have some other finals to take care of and we didn't really get to objects much, we barely touched classes.

I would though try it out this weekend and see how I get it to work. Thanks =)


@sanders -- checked out your blog and I like your story! I'm definitely going to bookmark it.

lloyddean
Dec 14, 2011, 06:18 PM
#include <iostream>
#include <string>
#include <sstream>

#if 1

template <class T>
void getInput(T& value, const std::string& strPrompt, const std::string& strRepromptError)
{
do
{
std::cout << strPrompt << std::flush;

std::string str;
getline(std::cin, str);

std::istringstream iss(str);
if ( iss >> value )
{
break;
}

std::cout << strRepromptError << std::endl;
} while ( true );
}

#else

template <class T>
void getInput(T& value, const std::string& strPrompt, const std::string& strRepromptError)
{
int decimal_points;
bool invalid;
std::string str;
do {
std::cout << strPrompt << std::flush;
getline(std::cin, str);

invalid = false;
decimal_points = 0;

std::string::iterator itr = str.begin();
if ( ('-' == *itr) || ('+' == *itr) )
{
itr++;
}

for ( ; itr != str.end(); itr++ )
{
if ( !isdigit(*itr) )
{

if ( ('.' == *itr) && (++decimal_points <= 1) )
{
continue;
}

invalid = true;
std::cout << strRepromptError << std::flush;
break;
}
}
} while ( invalid );

std::istringstream iss(str);
iss >> value;
}

#endif


int main(int argc, const char* argv[])
{
const char* pszError = "Invalid entry, please try again!\n");
float value;

while ( true )
{
getInput(value, "Give me a number: ", pszError);
if ( value >= 0.0f )
{
break;
}

std::cout << "Sorry, please provide a positive value!\n\n";
}

std::cout << "User provided value: " << value << std::endl;

return EXIT_SUCCESS;
}



As quick test try each of these versions of 'getInput'.

Neither accept leading or trailing spaces and it is questionable if they should.

Only one will handle trailing non-numerics or mutliple decimal points corrrectly.

subsonix
Dec 14, 2011, 07:07 PM
As quick test try each of these versions of 'getInput'.

Neither accept leading or trailing spaces and it is questionable if they should.

Only one will handle trailing non-numerics or mutliple decimal points corrrectly.


Here, I get the first version to ignore leading/trailing space. While the second version interpret it as part of the number. IMO, it's more natural to ignore it as in the first version. To me that seems like the most natural way to handle input, for a promt at least.

lights
Dec 14, 2011, 07:13 PM
@llyodean, that code is a bit too advance for what I'm doing. I can barely understand the code (knowledge isn't all there yet). Thank you I will try this out as well this weekend.

I'm probably going to create a function that checks my amount. I will pass the value of amount into the function that sanders posted through the link. And hopefully, it will return a value where my while statement will process.

lloyddean
Dec 14, 2011, 09:42 PM
Yes, I said as much concerning leading spaces above. But try things like 1.1.1 or 1.a or similarly wrong entries.

subsonix
Dec 14, 2011, 09:49 PM
Yes, I said as much concerning leading spaces above. But try things like 1.1.1 or 1.a or similarly wrong entries.

Hm, maybe I misunderstood you but:

"Neither accept leading or trailing spaces and it is questionable if they should."

But one does accept leading/trailing space, while the other doesn't. :)

lloyddean
Dec 14, 2011, 10:12 PM
Again, I said as much in an earlier posting. It's easily fixed. How about the other defects?

lights
Dec 15, 2011, 01:35 AM
Well, I went ahead and followed what Sanders had recommended and while it does check for letter input, it doesnt check for anything else like 5.2R instead of 5.24, typos is going to hurt but hey atleast it checks for letters lol.

I just call it into amount and so far it works for what its intended. I dont think I'm going to have time to check for trailing mistakes.

You guys are great! thanks!! :D

amount = errorCheck();


float errorCheck()
{
int counter = 0;
string input = "";
float amount = 0;

while (true && counter < 2)
{
getline(cin, input);

stringstream myStream(input);

if (myStream >> amount)
break;
counter++;
cout <<endl <<setw(5) <<"" <<"INVALID NUMBER, PLEASE TRY AGAIN" <<endl;
cout <<endl <<left <<setw(5) <<"" << "ENTER AMOUNT: $";
}

return(amount);
}

jiminaus
Dec 15, 2011, 03:00 AM
lights, your use of indentation really blows. It's confusion to read and misleading.

Why while (true && counter < 2)? The true is redundant. It's completely equivalent to while (counter < 2), no?

lights
Dec 15, 2011, 08:49 AM
Sorry jimanus,

I had my code on my iPad when I posted that. I didn't realize it until you brought it up.

The counter counts the amount of error so that it will kick out after the 2nd try. My calling function then has a statement that ask to go to the main menu.


I'll fix it after my 2 finals today, then turn in my final project. Which works pretty good thanks to the people that helped...

lloyddean
Dec 15, 2011, 12:22 PM
lights,

Added whitespace trimming to front and back of input.
De-templatized 'getValue' which now expects 'value' refernece to be of type float.



#include <iostream>
#include <string>
#include <sstream>

#if 0

void getInput(float& value, const std::string& strPrompt, const std::string& strRepromptError)
{
do
{
std::cout << strPrompt << std::flush;

std::string str;
getline(std::cin, str);

std::istringstream iss(str);
if ( iss >> value )
{
break;
}

std::cout << strRepromptError << std::endl;
} while ( true );
}

#else

void getInput(float& value, const std::string& strPrompt, const std::string& strRepromptError)
{
std::string strTrimChars(" \t\r\n");

int decimal_points;
bool invalid;
std::string str;
do {
std::cout << strPrompt << std::flush;
getline(std::cin, str);

if ( std::string::npos != str.find_last_not_of(strTrimChars) )
{
str.erase(str.find_last_not_of(strTrimChars) + 1) ;
}

str.erase(0, str.find_first_not_of(strTrimChars)) ;


invalid = false;
decimal_points = 0;

std::string::iterator itr = str.begin();
if ( ('-' == *itr) || ('+' == *itr) )
{
itr++;
}

for ( ; itr != str.end(); itr++ )
{
if ( !isdigit(*itr) )
{

if ( ('.' == *itr) && (++decimal_points <= 1) )
{
continue;
}

invalid = true;
std::cout << strRepromptError << std::flush;
break;
}
}
} while ( invalid );

std::istringstream iss(str);
iss >> value;
}

#endif


int main(int argc, const char* argv[])
{
std::string strError("Invalid entry, please try again!\n");

float value;

while ( true )
{
getInput(value, "Give me a number: ", strError);
if ( value >= 0.0f )
{
break;
}

std::cout << "Sorry, please provide a positive value!\n\n";
}

std::cout << "User provided value: " << value << std::endl;

return EXIT_SUCCESS;
}

lights
Dec 15, 2011, 01:14 PM
llyoddean! that code is so awesome, it works beautifully!

I wish I can use it since it checks for trailing errors and everything else.

Sadly, again, the code looks too advance for us students. But he never said we can't look at other header files and use some of those functions to implement an error check. Hmmm, I'm going to try to understand exactly how your code works and if I have time, maybe I can try to use it.

Thanks =)

lloyddean
Dec 15, 2011, 02:19 PM
Good for you!

Never turn in code that you can't explain how it works, just in case you get called on it.

lights
Dec 19, 2011, 02:50 PM
Thanks for all the help guys, As I stated in my other thread, I believe I got the highest programming score and extra credit points.

While I did not use the code you guys helped me with, I learned a lot from you guys.

I ended up going with a do while statement for the error checks. And my professor didn't even check for errors lol, except for negative numbers, but he did see it and he liked that I took the steps to try to do some basic error checks.