Thread: New guy with a error trapping question

  1. #1
    Registered User
    Join Date
    Jun 2014
    Location
    Texas
    Posts
    6

    New guy with a error trapping question

    Hello,

    I am studying C language and I have a question about using scanf ( bet it's an old question that crops up.)

    Now when I have a user input a integer or float or double I need to make sure they don't input anything else but what is supposed to be there.

    I understand using a character input and converting for integer but the other two I have grave doubts! Used to in COBOL (yep I'm an old CICS/MVS/COBOL guy) we used character input into an array and hunted down the '.', if any to covert (it WAS complicated!)

    Is there a function for this or does it have to be done by hand each time?

    Thanks,

    Deaf

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    The usual technique is to use fgets() to read a line of data from the user, and then interpret the contents of the string (for example, using sscanf() to read an integer or whatever is expected). If the content of the string contains what is expected, proceed, otherwise discard the string and go back.


    Computer programs generally don't have access to any means of preventing a user from typing in the wrong input at a keyboard, and therefore have to cope with invalid input. Preventing a user from doing unwanted things would require hardware and device driver support (for example, to electrocute the user who tries to enter the string "PoohBear" when the program prompts for an integer). However, such hardware is rarely available because it is unpopular with end users.
    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.

  3. #3
    Registered User Alpo's Avatar
    Join Date
    Apr 2014
    Posts
    877
    Quote Originally Posted by grumpy View Post
    Computer programs generally don't have access to any means of preventing a user from typing in the wrong input at a keyboard, and therefore have to cope with invalid input. Preventing a user from doing unwanted things would require hardware and device driver support (for example, to electrocute the user who tries to enter the string "PoohBear" when the program prompts for an integer). However, such hardware is rarely available because it is unpopular with end users.
    You can include <skinner.h> and reward the user with either Snickers, or for invalid input, a mechanical boxing glove to pop out of the screen and sock them in the face. Or for a laugh, set the functions with rand(), and generate a horrible sense of paranoia in everyone who comes near the program.

    </joke>

  4. #4
    Registered User
    Join Date
    Jun 2014
    Location
    Texas
    Posts
    6
    Ok here is what I did.

    Using scanf I read in a char list and stopped it when it came to a '/n'.
    Then used strtod for conversion to double and istringstream to convert to integer.

    now... the string I use with scanf, 'buf', how can I inspect each byte to see if it's a number between 0 and 9 OR a '.' (for the doubles.)

    I presume there is come kind of way to peek into a string.

    Then if it's none of the above I can have a goto go back and force a redo of the input.

    Thanks,

    Deaf

  5. #5
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    if you are studying C why are you talking about using istringstream which is part of C++?
    if you are using scanf why do you need something else to parse input?

    As was suggested - use fgets + sscanf or fgets+strtod - both methods allow to exemine "unread" part of the buffer
    In sscanf you will use %n format to find the index of the first unread character. In strtod you will pass pointer to pointer which will be set to first unread character.

    You should examine it and it it is not '\n' - when something is wrong and you can discard buffer and ask user to enter the number again.

    And of course it is better done without goto
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  6. #6
    Registered User
    Join Date
    Jun 2014
    Posts
    79
    I have been getting good results with a code like this:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    
    int main() {
        char buff[24];
    
        /* reads max 1 character from stdin */
        fgets( buff, 2, stdin );
        /* shows what has been read */
        printf( "\nYou wrote: %s\n\n", buff );
         /* clears any "extra" character */
        fflush( stdin );
    
        /* reads max 19 characters from stdin */
        fgets( buff, 20, stdin );
        /* shows what has been read */
        printf( "\nYou wrote: %s\n\n", buff );
         /* clears any "extra" character */
        fflush( stdin );
    
        return 0;
    }
    If you need to get ints or floats as input instead of strings, just feed atoi() or atof() with the buff string.

  7. #7
    Registered User
    Join Date
    Jun 2014
    Posts
    79
    This could be a different approach:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <locale.h>
    
    
    int IsNumber( const char *s ) {
        const char *p = s;
        char point;
    
        point = *localeconv()->decimal_point;
    
        while( *p ) {
            if( (*p<'0' || *p>'9') && (*p!=point) )
                return 0;
            else p++;
        }
    
        return 1;
    }
    
    
    int main() {
        char buff[16];
        double data;
    
        do {
            /* asks for a number */
            printf( "\n\nPlease, give me a number: " );
            /* reads max 15 characters from stdin */
            fgets( buff, 16, stdin );
            /* clears any "extra" character */
            fflush( stdin );
            /* clears the '\n' character at the end of buff */
            buff[strlen(buff)-1] = '\0';
        } while( !IsNumber(buff) );
    
        /* converts the string into a number */
        data = atof( buff );
    
        /* shows the number typed by the user */
        printf( "\nYou've just typed %.2lf\n\n", data );
    
        return 0;
    }
    Last edited by aldo_baldo; 06-16-2014 at 04:36 PM.

  8. #8
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    @aldo_baldo:
    Welcome to the forum!

    Maybe you're aware, but in case not, we generally don't hand out complete solutions here (check out the forum guidelines and homework policy if you haven't yet), as it typically inhibits learning. In this case it might not be such a big issue since the OP appears to have significant programming experience, however it's best to generally avoid it, and to simply point the OP in the right direction with some hints. Again, not so critical here, but definitely keep this in mind for threads that are homework/school related.

    The second issue is with the content of the code you posted. You definitely get bonus points for using code tags, proper indentation, int main() and return 0; and for having clear comments on most of it (but not the IsNumber function which is arguably the least obvious code). However:

    First and most important, fflush(stdin) results in undefined behavior meaning anything or nothing at all may happen. Read this link for more info. You may get away with it on some implementations/platforms, but it's liable to break on many others. Best to avoid it, or use a portable version like here.

    Second, if you are posting example code, make the effort to post code that uses best practices, even in short snippets. It encourages the OP and anybody else who is here reading threads related to a problem they're facing, to use those practices themselves. A few things I note in your code:

    • Use of "magic numbers". Define a constant with a sensible name and use it everywhere possible. For buffer lengths being passed to functions like fgets, usually sizeof(buff) is preferred since, you may change the name of the constant, and forget to change one of the fgets calls, allowing for possible buffer overflow.
    • Your "clear the \n" line has a potential bug if buff doesn't end with a \n. Recall that fgets does not guarantee that there will be a \n in the last spot. What if the input ends with EOF (either user-signaled, or via input redirection)? What if the user types 20 chars and fgets only reads the first 15? There's a safer solution using strchr here.
    • You should prefer standard library functions where possible. Instead of checking *p for <0 or >9, use the isdigit() function in ctype.h. Instead of writing your own IsNumber(), you could use strtod, checking errno to ensure a complete, successful conversion and checking endptr if you want to ensure there was no garbage at the end of the numeric input. The reasons for this are the standard ones: more complete checking of input, more efficient, better tested.

  9. #9
    Registered User
    Join Date
    Jun 2014
    Location
    Texas
    Posts
    6
    Well I did not expect such code guys.

    I was thinking making input a text string and searching it for '0'-'9' (as above) and a '.'. and if all the characters in the were ok, set a flag to '1 to symbolize the input was good and set the flat to '0' it something else resided in any byte. Then convert to integer or float as needed.

    So, is there a function or way to make an array take the input and make each byte a separate element?

    Will:
    char astring[10];scanf( "%s", astring );

    Put each byte into a element of astring?

    Thanks,

    Deaf

  10. #10
    Registered User
    Join Date
    Jun 2014
    Posts
    79
    Many, many, many thanks for your reply, as it lets me learning some things I didn't ever consider (please, be aware that I am a self-taught, amatorial "programmer"). Please let me reply to some of your points, hoping I'm not doing any harm to the forum.

    You wrote: "First and most important, fflush(stdin) results in undefined behavior meaning anything or nothing at all may happen."

    I found fflush() for the first time a couple of weeks ago, reading a post on another forum, and I took it for granted its reliability. I see the point in calling over and over getc() in order to flush the input buffer. Nevertheless, I can't imagine any way to avoid getting "stuck" with an already empty buffer. Is there any?

    You wrote: "Second, if you are posting example code, make the effort to post code that uses best practices, even in short snippets."

    ...provided that I'm aware of such best practices!!!

    You wrote: "Use of 'magic numbers'. Define a constant [...]"

    In regular code I usually make use of #define or constants. In this particular case I put a number for brevity sake, relying on the fact that the code is short and self-explaining.

    You wrote: "[...] fgets does not guarantee that there will be a \n in the last spot."

    Duh! You are RIGHT! I missed that point. Now I have been warned and I know. I won't make that mistake again. The strchr() trick rules! Many thanks.

    You wrote: "Instead of checking *p for <0 or >9, use the isdigit() function in ctype.h."

    Yes, but does ctype.h include any way of testing the existence of a decimal point?

    You wrote: "Instead of writing your own IsNumber(), you could use strtod() [...]"

    Usually I do as you described. In this particular case I was trying to stay as close as possible to what the other poster asked: check that the user didn't type any inappropriate character. For some reasons (?), I have been thinking that the IsNumber() approach would have been clearer. Looks like I was wrong.

    P.S. Please be patient with my poor english -- I'm a 50 years old Italian self-taught english writer, so maybe I make Bad Mistakes here and there.

  11. #11
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by Deaf Smith View Post
    So, is there a function or way to make an array take the input and make each byte a separate element?

    Will:
    char astring[10];scanf( "%s", astring );

    Put each byte into a element of astring?
    Yes, that is what a string is in C. A bunch of adjacent bytes each with one char in it, and ending with a null byte, i.e. '\0'. It is often implemented as a simple char array. scanf with the %s modifier will do what you ask, though you should to be sure that input wont overflow astring: scanr("%9s", astring). That allows 9 chars plus a null terminator. That does not work well if you change the size of astring however, since you have to go change all the scanf calls. That is why many of us suggest using fgets to read a whole line. Like so:
    Code:
    #define MAX_LEN 10  // in reality I would make this large enough for anything the user might enter, on the order of several hundred bytes or more
    char astring[MAX_LEN];
    fgets(astring, sizeof(astring), stdin);  // if you change MAX_LEN or astring (e.g. to be of size MAX_STR), your fgets call will always read the right size
    char *p = strchr(astring, '\n');  // look for a new line
    if (p != NULL) {
        *p = '\0';  // newline exists, replace it with null
    }
    Now you have a string from the user
    Quote Originally Posted by Deaf Smith View Post
    I was thinking making input a text string and searching it for '0'-'9' (as above) and a '.'. and if all the characters in the were ok, set a flag to '1 to symbolize the input was good and set the flat to '0' it something else resided in any byte. Then convert to integer or float as needed.
    You could, but it gets complicated fast. For example, what if the user enters multiple '.' characters? What about "scientific notation" input? That is, what if the user enters 123.45e-03 (equivalent to 0.12345). The standard float/double conversion functions like scanf, atof, strtod accept such input and users may try to enter that. Now your IsNumber check just got way more complicated. Besides, scanf, atof and strtod will handle all that for you anyway, and ensure there's only zero or one '.' characters and only valid digits. It's not a bad approach in general, but input validation is a pain, and you're better off letting well-tested library functions do as much of it as possible. The only reason to write your own is as a learning exercise, or to appreciate just how difficult such a seemingly simple task can be.

  12. #12
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by aldo_baldo View Post
    Many, many, many thanks for your reply, as it lets me learning some things I didn't ever consider (please, be aware that I am a self-taught, amatorial "programmer"). Please let me reply to some of your points, hoping I'm not doing any harm to the forum.
    No harm done. We're happy to have you aboard. I think just about everybody on this board is learning stuff here on a regular basis.
    Quote Originally Posted by aldo_baldo View Post
    Nevertheless, I can't imagine any way to avoid getting "stuck" with an already empty buffer. Is there any?
    Sadly, there is no portable way. Anything like a "peek" function that can be used to check for an empty input buffer, is platform/implementation specific.
    Quote Originally Posted by aldo_baldo View Post
    ...provided that I'm aware of such best practices!!!
    Now you are aware of at least a few
    Quote Originally Posted by aldo_baldo View Post
    Duh! You are RIGHT! I missed that point. Now I have been warned and I know. I won't make that mistake again. The strchr() trick rules! Many thanks.
    I keep a copy of the C standard (here) at hand for the gritty details of the language, but for quick function lookup, usually Googling "man 3 <function name>" will work. linux.die.net comes up as the first site often. man pages are a *NIX thing, but they usually do a good job of making clear the standard C behavior (C89/C99/C11) from extensions (POSIX, BSD, GNU, etc).
    Quote Originally Posted by aldo_baldo View Post
    Yes, but does ctype.h include any way of testing the existence of a decimal point?
    No, but I wasn't referring to replacing the decimal point portion of the if condition, just the digit comparisons. Something like:
    Code:
    if (!isdigit(*p) and *p != point)
    It's fairly trivial change, but it does two things: keeps you from worrying about things like mixing up <, <=, > and >=, or typing 0 and 9 instead of '0' and '9'. Also, instead of some random less than/greater than that would likely need a comment for clarity, it makes it clear what you are really checking: is or is not, this character, a valid digit? It's usually best if your code, via good organization and good function/variable names, is "self-documenting" wherever possible.

    Just a small related note, the C standard does guarantee that '0'...'9' are consecutive chars, but it does not guarantee that the alphabet is consecutive, i.e. there may be other characters between 'a' and 'z' or 'A' and 'Z', and the letters may appear "out of order" (out of standard American/English alphabetical order at least).
    Quote Originally Posted by aldo_baldo View Post
    P.S. Please be patient with my poor english -- I'm a 50 years old Italian self-taught english writer, so maybe I make Bad Mistakes here and there.
    People with poor English skills generally don't use know or use words like amatorial . Your English is better than many native speakers/writers I've known over the years.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Causing and trapping SIGSEGV
    By ampersand11 in forum C Programming
    Replies: 2
    Last Post: 01-22-2008, 05:26 PM
  2. Trapping input
    By dragon_man in forum C++ Programming
    Replies: 2
    Last Post: 05-13-2003, 09:07 PM
  3. error trapping ??
    By mellisa in forum C++ Programming
    Replies: 4
    Last Post: 02-14-2003, 09:31 PM
  4. trapping exit??
    By spike232 in forum C Programming
    Replies: 4
    Last Post: 04-03-2002, 06:27 AM
  5. trapping keystokes from the keyboard
    By dills101 in forum C++ Programming
    Replies: 1
    Last Post: 10-09-2001, 02:25 AM