Thread: Wrong input handling with cin

  1. #1
    Registered User
    Join Date
    Aug 2006
    Posts
    1

    Wrong input handling with cin

    Hello,

    I am trying to write code that will accept user input, and any non-integer value will return an error message followed by a prompt to re-enter.

    My current method of doing this works fine if a user enters characters, however if the user enters a float my program becomes confused.

    Whats the best way to do this?

    Here's my code:

    Code:
    do {
      test=false;
      cout << "\nEnter steps per turn\n\n> ";
      if (((cin >> spt)==0)) //Input and error handling
      {
       cin.clear(); 
       cin.ignore(10000,'\n');
       cout << "\nOnly positive integers please!\n";
       test=true;
      }
    }while(test);
    Thanks a bunch!

    J G

  2. #2
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >Whats the best way to do this?
    Use getline to read a string and then do your conversions instead of letting cin handle it. That way you can more easily check for and recover from errors. The FAQ has examples.
    My best code is written with the delete key.

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    I prefer to check the character after the last one read in. This will be '\n' if the user entered a number and only a number:
    Code:
      if (!(cin >> spt) || cin.get() != '\n') //Input and error handling
      {
       cin.clear(); 
       cin.ignore(10000,'\n');
       cout << "\nOnly positive integers please!\n";
       test=true;
      }
    It also has the added benefit of removing the trailing newline on good input, so if you mis cin >> and get or getline you won't have to worry about that issue.
    Last edited by Daved; 08-23-2006 at 11:13 AM.

  4. #4
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    What if the user enters a number followed by EOF instead of a newline? (I'm sure there's a shell that supports this.) What if the input is redirected from a file which contains a number but no newline?
    Code:
    C:\>copy con input
    12^Z
    C:\>get_number < input
    You might have an infinite loop.

    Code:
    cin.ignore(10000,'\n');
    ->
    Code:
    cin.ignore(std::numeric_limits < int >::max(), '\n');
    From http://faq.cprogramming.com/cgi-bin/...&id=1043284392
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  5. #5
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    Code:
    cin.ignore(std::numeric_limits < int >::max(), '\n');
    ->
    Code:
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    This code is essentially discarding all potential characters in the streambuf, so it's smarter to use the data type that best reflects that size. For maximum portability, include <ios> to get access to std::streamsize.
    My best code is written with the delete key.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You should put that in the FAQ.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> You might have an infinite loop.
    Perhaps on a file redirect, but I don't get one on regular console input. What code would you add to make it work when input is redirected to a file that ends without a newline?

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Something like " && !cin.eof()".

    [edit] That is,
    Code:
    if (!(cin >> spt) || (cin.get() != '\n' && !cin.eof()))
    [/edit]

    [edit=2]
    . . . I don't get one on regular console input.
    Even if you enter this? (Sorry, I don't have a compiler on this computer.)
    Code:
    Enter a number: 5^Z
    ie, <5><CTRL-Z><ENTER>.
    [/edit]
    Last edited by dwks; 08-24-2006 at 03:37 PM.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    So the "best" way is:
    Code:
    #include <iostream>
    #include <limits>
    
    int main()
    {
        int value = 0;
        // Prompt.
        while ((!(std::cin >> value) || std::cin.get() != '\n') && !std::cin.eof())
        {
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            // Error message.
            // Re-prompt.
        }
    }

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Yes, but I think you may need to include <ios> for std::streamsize.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    Wouldn't it be nice if all that error checking was encapsulated into a function that just returned true or false based on whether the input succeeded or failed? Heck, it would be great if that function worked with strings, too, or even wide character streams. Wait, it's already been done:
    Code:
    #include <iostream>
    #include "basic_input.h"
    
    int main()
    {
        int value = 0;
        // Prompt.
        while (!jlou::basic_input(std::cin, value))
        {
            // Error message.
            // Re-prompt.
        }
    }
    Here is the basic implementation for non-string types.
    Code:
    namespace jlou
    {
        template<typename charT, typename traits>
        inline std::basic_istream<charT,traits>& ignore_line(std::basic_istream<charT,traits>& is, charT delim)
        {
            return is.ignore(std::numeric_limits<std::streamsize>::max(), delim);
        }
    
        template<typename charT, typename traits, typename T>
        inline bool basic_input(std::basic_istream<charT,traits>& is, T& x, charT delim)
        {
            std::ios::fmtflags old_flags = is.flags();
            if ((is >> std::noskipws >> x) && (is.eof() || is.get() == delim))
            {
                is.flags(old_flags);
                return true;
            }
            is.flags(old_flags);
    
            x = T();
            is.clear();
            ignore_line(is, delim);
            return false;
        }
    }
    The full definition that handles C and C++ strings is in the small attached file. My old version worked on VC++ 6, but this one doesn't yet (if you'd like that version just ask).

    If you use this and can give it input that causes an infinte loop or unexpected behavior, or leaves cin in an incorrect state following the input, let me know, although I think I'm close to catching all potential errors.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Code:
    inline bool basic_input(std::basic_istream<charT,traits>& is, T& x, charT delim)
    You should have a default value for delim, and maybe some of the other arguments, too.

    [edit] Could you PM me that header file? This computer can't open it. "This operation has been cancelled due to restrictions in effect on this computer. [...]" [/edit]
    Last edited by dwks; 08-24-2006 at 04:31 PM.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  13. #13
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    There is, check the attached file. I had a reason for not making cin the default value for is, but I did this a long time ago, so I don't remember what that was.

  14. #14
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Well, you don't have a reason now, right?
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  15. #15
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,090
    Yes. I just don't remember what it is. If I think of it I'll tell you. Until I do, or until I think it through, it's staying as is.

    Edit: One reason is so that beginners understand that they are using cin for input. I don't want the gap between basic_input and cout to be too large.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. can someone help me with these errors please code included
    By geekrockergal in forum C Programming
    Replies: 7
    Last Post: 02-10-2009, 02:20 PM
  2. CIN and exception handling
    By OldSchool in forum C++ Programming
    Replies: 4
    Last Post: 05-14-2006, 05:02 PM
  3. cin not allowing input after first use of function
    By Peter5897 in forum C++ Programming
    Replies: 5
    Last Post: 01-31-2006, 06:29 PM
  4. Other than cin, how can a get a program to take input?
    By deathstryke in forum C++ Programming
    Replies: 15
    Last Post: 01-30-2003, 02:56 PM