Thread: Detecting illegal characters

  1. #1
    Registered User
    Join Date
    Feb 2009
    Posts
    6

    Question Detecting illegal characters

    I have just created a program to act as a quadratic root solver. The user inputs the a, b and c values from their ax^2 + bx + c =o equation and the program finds the roots and prints them on the screen or if there are no real roots it prints "There are no real roots to this quadratic equation".

    However, the requirement of the program is to be able to cope without crashing if illegal characters (e.g. letters or symbols or .1 etc.) are entered. Ideally it would need to print on the screen "You have entered an illegal character".

    Is there a very simple way of coding this function into the program?

    The existing code is shown below.

    Thanks.

    Paul

    Code:
     #include <stdio.h>
    #include <math.h>
    
    
    main()
    {
          float a, b, c, determinate, root1, root2;
          
          printf("Enter the required a followed by b followed by c and the program will calculate the roots for you: \n");
          scanf("%f %f %f", &a, &b, &c);
          printf("\n");
          
          determinate = (b*b)-(4.0*a*c);
                
                if (determinate>0)
                        {root1 = (((-b)+sqrt(determinate))/(2*a));
                        root2 = (((-b)-sqrt(determinate))/(2*a));
                        printf("Root 1 = %8.5f      Root 2 = %8.5f\n", root1, root2);}
                        
                if (determinate == 0)
                        {root1 = (((-b)+sqrt(determinate))/(2*a));
                        printf("Repeated root = %8.5f \n", root1);}
                        
                if (determinate<0)
                       printf("There are no real roots to this quadratic equation.\n");
                       
                       
                printf("\n");
          
          system ("pause");}

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    scanf returns the number of successfully read in. So check the return value from scanf, and if it's not 3, then something was not entered correctly. You then should clean up the input buffer, which there is a FAQ entry that describes how that is done.

    Note that there is a subtle difference between what I am suggesting and what you are specifically asking for. Conceptually they lead to the same thing: The program behaves correctly with invalid input. But it is much harder to determine what the input was, than to determine IF it is correct or not.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Feb 2009
    Posts
    138
    scanf will return 3 if all three numbers are valid. you can do this.
    Code:
    if (scanf("%f %f %f", &a, &b, &c) != 3)
    {
        fprintf(stderr, "bad input\n");
        exit(EXIT_FAILURE);
    }

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    You should learn to use fgetc() to do this instead of scanf().

    Remember, input is buffered, so if you ask for a line, you can then iterate thru it one byte at a time, giving you the opportunity to parse it however you like, etc.

    Have a look at the getrow() function in this post.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    And don't quit on failure; ask the user to input something again.
    Nothing is more frustrating than when the application quits because you entered something wrong.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I have deleted some posts that ended up in a flamewar.

    Anyway, Meldreth wanted to make a point, namely, that besides just asking the user to input again, you need to ignore the invalid input that is left in the buffer.

    One way to accomplish the task is to use fgets() + sscanf() instead of using scanf() directly.
    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

  7. #7
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    One way to accomplish the task is to use fgets() + sscanf() instead of using scanf() directly.
    e.g.
    Code:
    char line[BUFSIZ];
    do {
        if(!fgets(line, sizeof line, stdin)) {
            printf("Error in input or end of input reached\n");
            exit(1);
        }
    } while(sscanf(line, "%f%f%f", &a, &b, &c) != 3);
    Or you could continue to use scanf(), I suppose . . .
    Code:
    while(scanf("%f%f%f", &a, &b, &c) != 3) {
        int c;
        while((c = getchar()) != '\n' && c != EOF) {}  /* discard input until newline */
        if(c == EOF) {  /* EOF = End Of Input */
            printf("Error in input or end of input reached\n");
            exit(1);
        }
    }
    I wouldn't recommend that over the former solution, though, since it still allows the user to enter newlines between numbers, and perhaps getting confused . . .

    BTW, I've never heard what I know as the discriminant called a "determinate" before.
    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.

  8. #8
    Registered User
    Join Date
    Feb 2009
    Posts
    6

    Question

    Thanks for your replies guys. I've tried using the scanf("%f %f %f", &a, &b, &c) != 3 idea and it does seem to determine when a letter is entered instead of a number and prints :"You have entered an illegal character".

    However as someone suggested, the invalid input is still in the input buffer and causes the following if statement to be true and run as well which is undesirable:


    Code:
    if (determinate<0)
                       printf("There are no real roots to this quadratic equation.\n");
    I tried to stop the if statement running by changing it to run if the determinate is <0 and if the number of successfully entered numbers is 3 as you can see below, but the if statement still runs.

    Code:
     if (determinate<0 && scanf("%f %f %f", &a, &b, &c) == 3);
                       {printf("There are no real roots to this quadratic equation. \n");}
    Any ideas?

    The full code is below.

    Code:
    #include <stdio.h>
    #include <math.h>
    
    
    main()
    {
          float a, b, c, discriminant, root1, root2;
          
          printf("Enter the required a followed by b followed by c and the program will calculate the roots for you: \n");
          scanf("%f %f %f", &a, &b, &c);
          printf("\n");
          
          discriminant = (b*b)-(4.0*a*c);
                
                if (scanf("%f %f %f", &a, &b, &c) != 3)
                   {printf("You have entered an illegal character.");}
                
                if (discriminant>0)
                        {root1 = (((-b)+sqrt(determinate))/(2*a));
                        root2 = (((-b)-sqrt(determinate))/(2*a));
                        printf("Root 1 = %8.5f      Root 2 = %8.5f\n", root1, root2);}
                        
                if (discriminant == 0)
                        {root1 = (((-b)+sqrt(determinate))/(2*a));
                        printf("Repeated root = %8.5f \n", root1);}
                
                if (discriminant<0 && scanf("%f %f %f", &a, &b, &c) == 3);
                       {printf("There are no real roots to this quadratic equation. \n");}
             
          
          system ("pause");}
    BTW determinate is the same as discriminant, I was taught to use determinate at school.

    Thank you

    Paul

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    As I already tried to pointed out, and as life seems to be demonstrating, scanf is a very poor choice for doing this kind of thing. You will be much happier if you just learn to use fgetc and low level parsing routines, whether that's today, or tomorrow, or next week, because it will have to happen or you will just get frustrated with programming and quit. I promise. They are much more secure and versatile.

    But by all means, bang away.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    The scanf function can read numbers separated by white space with relative ease (as dwks has demonstrated).

    fgetc, IMO, is the exception rather than the rule, because it leads to this....

    The post that you are alluding to has its' own problems: atoi is a good idea if you are sure a string contains digits, only, and if the conversion does not cause overflow at some point. Not to mention that a more stringent method, like strtol, at least checks for overflow. There's also strtod for floating point. Using such workhorses to their potential takes a bit of know-how, but I'd rather see examples using it than some other way, if you're going to really recommend rolling your own to someone.

    "... by all means, bang away."

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    "The post that I was alluding to" was not meant as a drop in replacement. The point is YOU COULD put some error checking into it because it works byte by byte BEFORE the atoi (or I guess the OP will want atof) call. Or, as you suggest, the strtol/strtod. But you are being intentionally deceptive about scanf. Sorry.

    Anyone who creates an overflow doing this needs a different kind of therapy, because I don't think I could do it if I tried. What on earth would are you going to overflow???
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Whether or not you intended it to be a drop-in replacement is meaningless in the context of what I actually said, since I took issue with how you used atoi in an example you thought was relevant here. I have just as much a right to evaluate examples as anyone else here.

    You can type numbers that are too small or too large for integers. atoi doesn't let you know that's the case, though, and you'll end up with a representation of some other number. That is what I mean--perhaps my vocab is rusty and my use of overflow incorrect--but things should be clear as crystal now.

  13. #13
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    CLear enough -- I'm just trying to do the OP a favour and I think you should have distinguished your argument "strtof vs. atof" (a good one) from your "scanf vs. fgetc" (a bad one, forget it, those apples, these oranges, this a box, that a fluted hi-ball glass).

    I'm here all day most days and easily 5-10%+ of posts (or more) are about why scanf didn't invent my computer:
    Code:
    #include <scan.f>
    
    int main(int scanf, char scanf) {
            scanf myscanf;
            while (1) {
                   scanf("%@$", myscanf);
                   if (scanf) scanf(myscanf);
            }
            return scanf();
    }
    "I am new to scanf programming and don't understand why C doesn't work."

    It's a useful function, IMO, but there may be a reason that there are, in fact, other functions for input as well.

    To concur with laserlight, IMO the (s)scanf stuff is usually better to work on an existing string and not so much for creating them.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    However as someone suggested, the invalid input is still in the input buffer and causes the following if statement to be true and run as well which is undesirable:
    This is not a suggestion, but an actual problem that needs correcting. You should reread dwks' post (and ignore the side discussion that MK and I are having, sorry).

    If the input doesn't match the format string, scanf returns a result you don't want. We expressed this in the program as:

    if ( scanf("%f%f%f", &a , &b , &c) != 3 )

    but scanf doesn't discard input for you. If you don't, scanf will keep failing on the same piece of input. That's why the extra loop that dwks' put in his scanf example is important.

    It's a useful function, IMO, but there may be a reason that there are, in fact, other functions for input as well.
    I would agree. But to be clear on this final point, I will take exceptions to solutions that take nothing away from a scanf solution. If you're going to recommend rolling your own with whatever, the examples better be able to run pretty clean. Yours didn't, that's all.
    Last edited by whiteflags; 02-27-2009 at 07:56 PM. Reason: quoted the wrong part of a post

  15. #15
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Code:
                if (discriminant<0 && scanf("%f %f %f", &a, &b, &c) == 3);
                       {printf("There are no real roots to this quadratic equation. \n");}
    This is the wrong thing to do for a few reasons . . . first of all, you have an extra semicolon there which will make what you think is body of the if execute all of the time. Secondly, you'll be getting more numbers every time you call scanf(). You don't want the user to have to enter their three numbers more than once.

    You really should read my previous example again, but maybe I can provide a more relevant one:
    Code:
    printf("Enter the required a followed by b followed by c and the program will calculate the roots for you:\n");
    /* try to read in three numbers */
    while(scanf("%f%f%f", &a, &b, &c) != 3) {
        /* scanf()'s attempt to read three numbers was unsuccessful */
        
        /* clear the input buffer; i.e., remove all characters until a newline */
        int c;
        while((c = getchar()) != '\n' && c != EOF) {}
    
        /* if the user has entered EOF (CTRL-Z on Windows), exit the program */
        if(c == EOF) {  /* EOF = End Of Input */
            printf("Error in input or end of input reached\n");
            exit(1);
        }
    
        /* prompt again for another three numbers */
        printf("Invalid input, please try again:\n");
    }
    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.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. A development process
    By Noir in forum C Programming
    Replies: 37
    Last Post: 07-10-2011, 10:39 PM
  2. Replies: 10
    Last Post: 07-10-2008, 03:45 PM
  3. help with text input
    By Alphawaves in forum C Programming
    Replies: 8
    Last Post: 04-08-2007, 04:54 PM
  4. SSH Hacker Activity!! AAHHH!!
    By Kleid-0 in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 03-06-2005, 03:53 PM
  5. Illegal Characters
    By Echidna in forum Windows Programming
    Replies: 3
    Last Post: 12-08-2002, 04:56 AM