input errors c++

Discussion in 'Mac Programming' started by lights, Dec 13, 2011.

  1. macrumors newbie

    Joined:
    Dec 13, 2011
    #1
    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.

    Code:
    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
    
    
     
  2. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
  3. macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #3
    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...
     
  4. macrumors 6502

    Joined:
    Apr 24, 2008
    #4
    So did the original program. Reformatting it, it basically says:

    Code:
    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...
     
  5. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #5
    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.


    Code:
    
    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.
     
  6. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #6
    Would the user typing 12f be considered an error? Or would amount be 12 considered OK?
     
  7. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #7
    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
     
  8. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #8
    Sorry I don't understand this part of your reply!
     
  9. macrumors 68040

    Joined:
    Feb 2, 2008
    #9
    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.

    Code:
        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.
     
  10. lights, Dec 13, 2011
    Last edited: Dec 13, 2011

    thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #10
    @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.
     
  11. lloyddean, Dec 13, 2011
    Last edited: Dec 13, 2011

    macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #11
    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.


    Code:
    #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;
    }
    
     
  12. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #12
    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 :(
     
  13. macrumors 6502

    Joined:
    Apr 24, 2008
    #13
    For an excellent summary, see here.

    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.
     
  14. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #14

    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.
     
  15. macrumors 68040

    Joined:
    Feb 2, 2008
    #15
    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)

    Code:
    #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.

    Code:
    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
     
  16. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #16
    @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.
     
  17. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #17
    Code:
    #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.
     
  18. macrumors 68040

    Joined:
    Feb 2, 2008
    #18

    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.
     
  19. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #19
    @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.
     
  20. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #20
    Yes, I said as much concerning leading spaces above. But try things like 1.1.1 or 1.a or similarly wrong entries.
     
  21. macrumors 68040

    Joined:
    Feb 2, 2008
    #21
    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. :)
     
  22. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #22
    Again, I said as much in an earlier posting. It's easily fixed. How about the other defects?
     
  23. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #23
    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();

    Code:
    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);
    }
    
     
  24. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #24
    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?
     
  25. thread starter macrumors newbie

    Joined:
    Dec 13, 2011
    #25
    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...
     

Share This Page