Thread: validation problem in a loop (newbie question)

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    242

    validation problem in a loop (newbie question)

    I've just started working my way through Gaddis' Starting out with C++, 5th ed. and have made it through chap. 5. I'm unhappy with my validation code on problem 9 under programming challenges and was hoping to get some help.

    For those who don't have the book, the problem is this: User should enter the number of floors in a hotel, then you ask for number of rooms on each floor and how many of those are occupied. Anyhow, it's easy to validate (and loop back until a valid number is entered) as long as the user actually enters a number.

    But where I have the problem is if the user enters something other than an integer. I kept trying it with the letter 'g' as user input. Ok, here's a part of my code (same problem occurs if you add Rooms and Occupied as variables):

    Code:
    #include <iostream>
    using namespace std;
    
    int main()
    {
    	int Floors;
    	bool Val = 0;
    
    	// enter floors
    	do
    	{
    		cout << "How many floors does the hotel have? ";
    		cin >> Floors;
    
    		if (cin.fail())
    		{
    			cout << "Invalid entry!\n"
    				<< "Close the program and try again.\n";
    			break;	// note this line!
    		}
    		else if (Floors < 1)
    			cout << "The hotel must have at least 1 floor.\n\n";
    		else if (Floors > 100)
    			cout << "The hotel cannot have more than 100 floors.\n\n";
    		else
    			Val = 1;
    	}
    	while (Val == 0);
    
    	return 0;
    }
    If I don't include the "break;" statement after the whole cin.fail() thing, then my MS Visual C++ Express compiler just keeps scrolling when the user enters 'g' and doesn't allow another entry. With the "break;" the program at least ends, and my error message is shown.

    What I'd like to do, though, is give the user repeated opportunities to enter valid data for the Rooms variable rather than just forcing a program exit on invalid data.

    Any solutions much appreciated--particularly if they only involve things I already know having made it to Gaddis, chap. 5. But I'm happy with any solution that I can follow as C++ newbie.

    Thanks!

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Read a string from cin. Interpret the contents of the string to see if the data entered is an integer. If so, interpret the data as an int. If not, discard the data, and then go back and read the next string from cin.

  3. #3
    Registered User
    Join Date
    May 2009
    Posts
    242
    so something like this (if "informal" code is ok without using the coding box):

    char Sent[6];

    cin.getline(Sent, 6);

    Up to here, cin should automatically succeed no matter what the user does.

    Sorry for my current ignorance, but what's the simplest way then to "interpret the contents of the string to see if the data entered is an integer"? If I can have some way of determining that (let's call it the fictitious command "criterion"), then all I have to do is this, I would think:

    if (criterion) Floors = static_cast<int>(Sent);

    Right?

    Also, do you know why the program just causes infinite scrolling if you run my original code without the break line?
    Last edited by Aisthesis; 05-09-2009 at 08:56 AM.

  4. #4
    Registered User
    Join Date
    May 2009
    Posts
    242
    Another idea, pursuing grumpy's suggestion:

    Couldn't we also just initialize Floors to 0, then do the cin.getline method above and then with no if condition at all just say:

    Floors = static_cast<int>(Sent);

    It seems to me that this operation will simply fail if Sent isn't an integer, so Floors should still be 0 (?), and we should run the same loop as before with the error message requiring a minimum of 1 floor--unless it causes infinite scrolling as before (will test later, on the run right now).

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    You could read in as a string and parse the string with a stringstream as grumpy suggests. I think that might be a bit more advanced than what you need right now, though.

    For now, I'd suggest just using clearing cin of the failbit. You also have to ignore the bad characters. The way I like to suggest reading in integer input is to do something like this:
    Code:
    while (!(cin >> Floors))
    {
        cin.clear(); // clear failbit
        cin.ignore(1000, '\n'); // ignore bad input characters
        cout << "Invalid entry!\n"
            << "Close the program and try again.\n";
    }
    
    // Here you know Floors is a valid int.

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Daved View Post
    You could read in as a string and parse the string with a stringstream as grumpy suggests. I think that might be a bit more advanced than what you need right now, though.
    I coughed when I read that. Since when are the actions of reading a string, constructing a stringstream that can read the string, and extracting data from the stream classified as "advanced" operations?????

    Quote Originally Posted by Daved View Post
    For now, I'd suggest just using clearing cin of the failbit. You also have to ignore the bad characters. The way I like to suggest reading in integer input is to do something like this:
    Code:
    while (!(cin >> Floors))
    {
        cin.clear(); // clear failbit
        cin.ignore(1000, '\n'); // ignore bad input characters
        cout << "Invalid entry!\n"
            << "Close the program and try again.\n";
    }
    
    // Here you know Floors is a valid int.
    That technique will also loop forever when it encounters end of file. End of file will be encountered should the user enter the keystrokes corresponding to end of file (eg CTRL-D under several flavours of unix, CTRL-Z under windows). It will also be encountered should standard input be redirected so it comes from a file (a trivial thing for the user to do under unix, slightly harder to do but still possible under other operating systems).

    A technique that relies on a human user being well-behaved and compliant with arbitrary needs that suit the programmer hardly strikes me a good solution to the problem.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  7. #7
    Registered User
    Join Date
    May 2009
    Posts
    242
    I fear Dave is right in his assessment of my beginners' skill level, so please bear with me (I'll try to keep my number of totally stupid posts to a minimum).

    I'll try Dave's suggestion. That I understand. Even if it does fail when the user does certain things, as grumpy suggests, I'm pretty sure it will fail a lot less than my current method: Hitting CTRL-Z, etc. is a lot harder to do than just mistakenly typing in a letter rather than a number.

    Thanks very much to grumpy, too. I do appreciate catching a glimpse of how someone more versed in the language addresses these problems, but it will no doubt take me a while to have sufficient fluency in the language to be able to use this.

  8. #8
    Registered User
    Join Date
    May 2009
    Posts
    242
    This code seems to produce the desired results (largely copied from Dave):
    Code:
    while (cin.fail())
    		{
    			cin.clear(); // clear failbit
    			cin.ignore(1000, '\n'); // ignore bad input characters
    			cout << "Invalid entry!\n";
    			cout << "How many floors does the hotel have? ";
    			cin >> Floors;
    			cout << "The hotel has " << Floors << " floors.\n";
    		}
    Interestingly, if I use !(cin >> Floors) the program just stops rather than scrolling, but replacing it with cin.fail() here seems to work. Hmmmm... I would've figured the values are exactly the same, but something is clearly different there.

    (obviously haven't tried grumpy's "malicious user" scenario and don't intend to at this point, as I'm happy just to make it work for a sloppy or moderately bad user and don't feel like I'm ready for worse than that)

  9. #9
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> Since when are the actions of reading a string, constructing a stringstream that can read the
    >> string, and extracting data from the stream classified as "advanced" operations?????

    They're not advanced at all, but they are still more advanced than clear() and ignore(). If the OP has never used or learned stringstream, then adding that class to his or her repertoire is a little bit more work than just clearing the stream and ignoring the bad characters.

    As to whether my suggestion is the best solution or not, I won't argue that it is in all cases. I do think it is the simplest solution that is effective enough for what the OP is doing right now.

    Aisthesis, using grumpy's method isn't that much more advanced, you just need to be familiar with stringstreams in addition to how input streams work in general. It is worth learning if you have some time, it shouldn't take you that long to be proficient enough in C++ to understand it.

  10. #10
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> This code seems to produce the desired results (largely copied from Dave):

    That code won't work as well if the user types bad input twice. It will claim "The hotel has 0 floors." (or some garbage number) incorrectly. What was the problem with your modification of my suggestion?

  11. #11
    Registered User
    Join Date
    May 2009
    Posts
    242
    What happened when I used your suggestion (with !(cin >> Floors) as test for the while loop, was that the program just stopped and would neither repeat the loop nor go beyond it to the next step. When I modified it by substituting cin.fail() for !(cin >> Floors) was that it worked just fine under the conditions I was having problems with. With cin.fail() I'm pretty sure I tried entering a (harmless) non-integer a few times, and it still worked.

    I didn't test user entries such as those grumpy mentioned or scenarios where user sits on a key, hence possibly filling more than 100 character spots. But I'm happy with covering as many bad inputs as I can at this point.

    Hmmm... on strings, Gaddis doesn't even introduce that variable type until chap. 10 (I'm doing the exercises to chap. 5 now, and up to now he's used char Var[5] to define a string variable with up to 5 places), so I should actually be there before too terribly long (covered the first 5 in 3 weeks, but the further I get, the slower it goes (more to digest and put together).

    I'm not sure whether to jump ahead or not. I have no doubt that it's important and basic (the book is just supposed to be an intro after all), but I'm sure there's also plenty of other important and basic stuff in between that I currently don't know either. For the moment, I'm just trying to do as many of the exercises as I feel are needed to be solid in the material covered up to now--and get the validation as airtight as I can for now.

    Hopefully the string stuff will make all of this seem really uncomplicated to me once I get there, but as you surmised, I'm not quite there yet.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Aisthesis
    Hmmm... on strings, Gaddis doesn't even introduce that variable type until chap. 10 (I'm doing the exercises to chap. 5 now, and up to now he's used char Var[5] to define a string variable with up to 5 places)
    Looking at the table of contents of the 6th edition of the book, I'd say that it looks okay, though the introduction of C-style strings before std::string is regrettable, as you now see. Be careful though: if Var is an array of 5 chars used as a C-style string, it can hold up to 4 chars, with 1 char reserved for the null terminator.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. whats the problem....
    By vapanchamukhi in forum C Programming
    Replies: 3
    Last Post: 09-05-2008, 12:19 PM
  2. Input Validation Question
    By zackboll in forum C Programming
    Replies: 14
    Last Post: 10-12-2004, 12:05 AM
  3. Loop problem: Asks a question twice!!!
    By Rhodium in forum C Programming
    Replies: 5
    Last Post: 07-04-2003, 09:47 AM
  4. infinite loop problem
    By Gil22 in forum C++ Programming
    Replies: 3
    Last Post: 03-07-2003, 03:24 PM
  5. KEYSCANNING within a LOOP, Newbie Question
    By Robert_Ingleby in forum C++ Programming
    Replies: 8
    Last Post: 11-23-2001, 06:49 AM