Thread: Accepting formatted input

  1. #1
    Registered User
    Join Date
    May 2010
    Posts
    35

    Accepting formatted input

    I've read various google links and everyone seems to have a different way for dealing with formatted input in C and non of them seem to work for me so I'm looking for any suggestions. I want to accept an integer, a string(max 15), a string(max 20), and a float entered all at once. I've tried the following ideas but none work. I honestly don't care what approach will work, I'm just trying to find something. These are the 2 closest things I've gotten working, the first one works on correct input but fails on incorrect input (enters an infinite loop and never returns to the menu), and the 2nd just enters an infinite loop on both cases.

    Code:
    case 'i':
    {
       struct node* new = malloc(sizeof(struct node));
       int correct = scanf("%d%15s%20s%f", &new->custId, &new->Name, &new->Surname, &new->accBalance);
       if (correct == 4)
       {
          add(&head, new);
       }
       else
       {
          printf("Invalid entries\n");
          free(new);
          int clearScanfBuffer = 0;
          while ((clearScanfBuffer = getchar()) != '\n');
       }
       break;
    }
    //This code works if you enter perfectly correct input but fails on incorrect input.
    I'd like to return to the menu to try a new option but it doesn't go there
    it just lets me keep typing whatever I want on the keyboard in some infinite loop
    Code:
    case 'i':
    {
       char myString[100];
       fgets(myString, 100, stdin);
       struct node* new = malloc(sizeof(struct node));
       int correct = sscanf(myString, "%d%15s%20s%f", &new->custId, &new->Name, &new->Surname, &new->accBalance);
       if (correct == 4)
       {
          add(&head, new);
       }
       else
       {
          printf("Invalid entries\n");
          free(new);
       }
       break;
    }
    //This just doesn't work at all.  It never exists some loop even on correct input.

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Well fgets + sscanf is my preferred choice. As long as both functions are used correctly then the input is good.

    The only thing I can tell you from what you posted is: Your second and third inputs are strings, and yet you pass sscanf a pointer to a pointer. It's not right.

    You're not asking about a syntax error. You're asking about a logical bug and I need to be capable of reproducing it in a program you post to help you fix it.

  3. #3
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    you could show us
    1. struct declaration
    2. code for looping
    3. sample of "correct" input
    4. sample of "incorrect" input

    could your strings include spaces?
    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

  4. #4
    Registered User
    Join Date
    May 2010
    Posts
    35
    Here is more complete code:

    A valid entry would be something like: 856 John McSmith 45.76 (spaces between each item and hit enter when done).

    Invalid entries would be something like: "John Smith 845 34.45" or "85 John Mac Smith 56.55" or "5f5 John Smith 77rr" etc

    Code:
    struct node
    {
       int custId;
       char Name[16];
       char Surname[21];
       float accBalance;
       struct node* next;
    };
    
    //Typical linked list functions that all work 100%
    
    int main()
    {
       //menu choice printed using printf
       //various options like 'i' for insert, 'r' for remove, 'p' for print list, etc
    
       struct node* head = malloc(sizeof(struct node));
       head = NULL;
    
       char choice;
       while ((choice = getchar()) != 'x' && choice != 'X')
       {
          switch (choice)
          {
              case 'i':
              {
                  struct node* new = malloc(sizeof(struct node));
                  int correct = scanf("%d%15s%20s%f", &new->custId, &new->Name, &new->Surname, &new->accBalance);
                  if (correct == 4)
                  {
                      add(&head, new);
                  }
                  else
                  {
                       printf("Invalid entries\n");
                       free(new);
                       int clearScanfBuffer = 0;
                       while ((clearScanfBuffer = getchar()) != '\n');
                  }
                  break;
              }
    
              case 'r':
              {
                 //some code
                 break;
              }
    
              case 'c':
              {
                 //some code
                 break;
               }
           }
        getchar();
       }
       return 0;
    }
    Last edited by fxtdr79; 06-28-2010 at 12:01 AM.

  5. #5
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    After using scanf(), the "\n" is left in stdin, so getchar() does nothing. That's the cause of your infinite loop, I think.

  6. #6
    Registered User
    Join Date
    May 2010
    Posts
    35
    Quote Originally Posted by User Name: View Post
    After using scanf(), the "\n" is left in stdin, so getchar() does nothing. That's the cause of your infinite loop, I think.
    If 4 valid entries aren't entered it goes into the else loop which should clear the entire input buffer since it loops around until it reaches a '\n' so I don't see how '\n' would be getting left in the system and causing problems.

  7. #7
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by fxtdr79 View Post
    If 4 valid entries aren't entered it goes into the else loop which should clear the entire input buffer since it loops around until it reaches a '\n' so I don't see how '\n' would be getting left in the system and causing problems.
    the problem is - you do not know if scanf left something in the input buffer...

    I would think the fgets/sscanf version should be more stable
    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

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    I'd add that when you use fgets and getchar together bad stuff can happen. You enter i\n first and getchar consumes the i. Then fgets is called to consume \n, which necessarily causes sscanf to fail, and sticks you in your "clear the buffer" code.

  9. #9
    Registered User
    Join Date
    May 2010
    Posts
    35
    Quote Originally Posted by vart View Post
    the problem is - you do not know if scanf left something in the input buffer...

    I would think the fgets/sscanf version should be more stable
    Do you know how to correct eh fgets/sscanf code I put in the top post to get it work correctly ?

  10. #10
    Registered User
    Join Date
    Jun 2010
    Posts
    27
    It will work out fine, if you just insert the space into sscanf():

    Code:
    sscanf("%d %15s %20s %f", &new->custId, new->Name, new->Surname, &new->accBalance);

  11. #11
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    No, that's actually a non-issue. What it won't fix is the problem I mentioned.
    I'd add that when you use fgets and getchar together bad stuff can happen. You enter i\n first and getchar consumes the i. Then fgets is called to consume \n, which necessarily causes sscanf to fail, and sticks you in your "clear the buffer" code.
    All he has to do if he wants to use fgets and sscanf is either use fgets for ALL of his input or find some way to skip the \n getchar left in the stdin stream.

  12. #12
    Registered User
    Join Date
    Jun 2010
    Posts
    27
    But I did try it. It worked.

  13. #13
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    So did I. Just to prove a point, I wrote this little C program.

    Code:
    #include <stdio.h>
    
    struct node
    {
        int custId;
        char name[16];
        char surname[21];
        float acctBalance;
    };
    
    int main (void)
    {
        struct node foobar;
        char myString[100];
        int c = '\0';
        int ok;
    
        printf ("Keep going? (x/X to stop):\n");
        c = getchar ();
        while (c != 'x' && c != 'X' && c != EOF) {
            printf ("Enter customers data (x/X to stop):\n");
            if (fgets (myString , sizeof myString , stdin) != NULL) {
                ok = sscanf (myString , "%d %15s %20s %f" , &foobar.custId ,
                foobar.name , foobar.surname , &foobar.acctBalance) == 4;
                if (ok) {
                    printf ("custId=%d\n" , foobar.custId);
                    printf ("name=%s\n" , foobar.name);
                    printf ("surname=%s\n" , foobar.surname);
                    printf ("acctBalance=%f\n" , foobar.acctBalance);
                }
                else {
                    fprintf (stderr , "Bad input try again\n");
                }
            }
            printf ("Keep going? (x/X to stop):\n");
            c = getchar ();
        }
        return 0;
    }
    
    /*
    Keep going? (x/X to stop):
    y
    Enter customers data (x/X to stop):
    Bad input try again
    Keep going? (x/X to stop):
    y
    Enter customers data (x/X to stop):
    Bad input try again
    Keep going? (x/X to stop):
    y
    Enter customers data (x/X to stop):
    Bad input try again
    Keep going? (x/X to stop):
    x
    */
    It's basically what's happening to him, right -- logically equivalent, and all that? Like I said, what you posted is a non-issue. I'd just be repeating what I said in my earlier post, unless you can explain some other reason why I never have the opportunity to input the customer data. You can't.

  14. #14
    Registered User
    Join Date
    Jun 2010
    Posts
    5
    but you tried it with a correct input or with a incorrect one?

    edit: I hadnt read the last post
    Last edited by Kais3r; 06-28-2010 at 08:29 AM.

  15. #15
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Whether my customer input was going to be correct or not is irrelevant since -- as I keep having to point out -- getchar leaves a \n in the stream, and I do not have an opportunity to enter customer data at all.

    Let's review what happens when fgets encounters \n:
    Quote Originally Posted by man fgets
    fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character in the buffer.
    If you run my program, you will see what happens when \n is the first thing fgets sees.

    Now, for the final time, fgets + sscanf will work fine: just use fgets for all your input or consume the newline getchar will leave behind.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Input class project (again)
    By Elysia in forum C++ Programming
    Replies: 41
    Last Post: 02-13-2009, 10:52 AM
  2. About aes
    By gumit in forum C Programming
    Replies: 13
    Last Post: 10-24-2006, 03:42 PM
  3. Structure and Linked List User Input Question
    By kevndale79 in forum C Programming
    Replies: 16
    Last Post: 10-05-2006, 11:09 AM
  4. Problems reading formatted input with sscanf
    By Nazgulled in forum C Programming
    Replies: 17
    Last Post: 05-10-2006, 12:46 AM
  5. accepting input while writing to screen
    By variable in forum C Programming
    Replies: 17
    Last Post: 02-06-2005, 10:14 PM