0

I am looking to have a user enter an 8 digit account number. The code I have seems to work for everything unless you have multiple letters, i.e.: 'bbb' as an input. If that happens, it runs the while loop 3 times, displaying the printf, but not asking for another input.

Any input on how to fix this, or a better way to do it is welcome!

Right now, I'm using:

#include <stdio.h>

int main()
{
int return_val = 0;
int account_number = 0;
int within_range = 0;

printf("Please enter your 8 digit account number:\n");
return_val = scanf("%d", &account_number);
getchar();
getchar();

if((account_number > 9999999) && (account_number < 99999999))
{
    within_range = 1;
}

while ((return_val != 1) || (within_range != 1))  
{
    printf("Invalid account number. Account number must be 8 digits.\n");
    printf("Please enter your 8 digit account number: \n");
    //scanf("%d", &clear);
    return_val = scanf("%d", &account_number);
    getchar();
    getchar();

    if((account_number > 9999999) && (account_number < 99999999))
    {
        within_range = 1;
    }
}

printf("Account #: %d", account_number);
}
Jesse Walton
  • 183
  • 1
  • 3
  • 11
  • 2
    You should probably read it as a string, then validate its length and numeric content, then finally convert to an integer. – jarmod May 31 '13 at 02:25

6 Answers6

1

Will it help if you read the input as a string of characters (using fgets) and parse it from there using sscanf? You will not have to worry about the extra getchars.

#include <stdio.h>

int get_acct_num()
{
    char line[80];
    int  acct_num;
    int return_val = 0;
    printf("Please enter your 8 digit account number:\n");
    fgets ( line, sizeof ( line ), stdin );
    return_val = sscanf(line, "%d", &acct_num);
    if ( return_val != 1 )
        return ( 0 );
    if ( ( acct_num < 10000000 ) || ( acct_num > 99999999 ) )
        return ( 0 );

    return ( acct_num );
}

int main()
{
    int account_number = 0;
    while ( ! ( account_number = get_acct_num() ) )
        printf("Invalid account number. Account number must be 8 digits.\n");


    printf("Account #: %d", account_number);
}
unxnut
  • 8,509
  • 3
  • 27
  • 41
1

In this case it is better to parse a string

#include <ctype.h>
...

char input[200];

scanf("%s", input);

int len = strlen(input);
int dig = 0;

if (len == 8) {
  for ( ; dig<len ; dig++) if ( ! isdigit(input[dig])) break;
}

if (dig == 8) printf("OK\n");
else printf("Not ok\n");

the code ensures we have 8 digits and nothing else in input (prints "OK").

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
0
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main()
{
    int account_number = 0;
    int inval = 0;
    char acc_buf[256];
    printf("Please enter your 8 digit account number:\n");
    scanf("%s", acc_buf);
    if (strlen(acc_buf) == 8) {
        for (int i = 0; i < 8; i++)
            if (!isdigit(acc_buf[i])) {
                inval++;
                break;
            }
    } else inval++;
    if (!inval) {
        account_number = atoi(acc_buf);
        printf("Account #: %d\n", account_number);
    }
    return 0;
}
perreal
  • 94,503
  • 21
  • 155
  • 181
0

May I suggest a little rewrite?

#include <stdio.h>
#include <string.h> /* this is for strlen */
#include <stdlib.h> /* this is for atoi */

int main()
{
    char input [55]; /* this is to store the user input */

    int account_number = 0;

    printf("Please enter your 8 digit account number:\n");

    while (fgets(input, 55, stdin)[0] == '\n')
        ;  /* this is a safer way to get input, loop until there is input, ignore new lines */

     account_number = atoi(input); /* convert to an int */

    if (account_number < 10000000 || account_number > 99999999) 
        return -1; 
    /* quit if invalid input */

    printf("Account #: %d\n", account_number);

    return 0;
}

EDIT: I've used fgets here and atoi as I think it would be good to be familiar with those functions. Having said that atoi is not necessarily the best way to convert to an number. Strtol is far more reliable but a little more complicated to use.

This is one way to use strtol in this context:

 char* temp = 0;
 account_number = strtol(input, &temp, 10); /* convert to an int */

More on the subject of converting strings to numbers here.

EDIT-2: Taking into account chux's comment, the loop can be constructed like this too:

char* out;
do
{
    out = fgets(input, 55, stdin);
}
while (out == NULL || out[0] == '\n')
    ;
Community
  • 1
  • 1
Nobilis
  • 7,310
  • 1
  • 33
  • 67
  • 1
    Under rare circumstances, an EOF (via redirected input) or I/O error causes `fgets()` to return NULL. In these exceptional situations, `fgets(input, 55, stdin)[0]` will likely crash. – chux - Reinstate Monica May 31 '13 at 03:52
  • Noted, thanks, I've updated my post to suggest a simple way to handle NULL. – Nobilis May 31 '13 at 04:15
0

I really don't like using scanf(), prefer fgets(), then sscanf().
Details below.
2 key lines:

if (fgets(buf, sizeof(buf), stdin) == NULL)
...
while (1 != sscanf(buf, " %8lu %c", &AccontNummner, &ch));

Solution

#include <stdio.h>
#include <stdlib.h>
// Get 8 digit account number.  Returns -1 on I/O error or EOF
// Parsing error just tries again.
long Get8DigitAccountNumber(void) {
  const char *prompt = "Enter 8 digit account number: ";
  unsigned long AccontNummner;
  char ch;  // Extra text
  char buf[1024];
  do { // or while (1)
    ch = '\0';
    printf(prompt);
    fflush(stdout);  // Appears to be needed on some systems.
    prompt = "Error, try again: ";  // Used on re-try
    if (fgets(buf, sizeof(buf), stdin) == NULL) {
      return -1;  // handle I/O error
    }
  // If not _exactly_ one 1-8 digit field parsed, then try again.
  // Leading and trailing whitespaces are OK
  } while (1 != sscanf(buf, " %8lu %c", &AccontNummner, &ch));
  return (long) AccontNummner;
}

int main() {
  long AccontNummner;
  while ((AccontNummner = Get8DigitAccountNumber()) >= 0) {
    printf("# %lu\n", AccontNummner);
  }
  return 0;
}

If you want to read exactly 8 digits ...

  int n1 = 0;
  int n2 = 0;
  } while ((1 != sscanf(buf, " %n%8lu%n %c", &n1, &AccontNummner, &n2, &ch) || ((n2 - n1) != 8));

Acceptable format: [optional whitespaces][1-8 digits][optional whitespaces][nothing more]
sscanf() format: " %8lu %c".
Use %u instead of %d to not allow '-'.
Explicitly allow optional leading and trailing spaces.
The %c catches any non-white char after the 8-digit number.
Scanning anything via %c causes sscanf() to return 2.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
-1

It's been a while since I fooled with formatted input in C but try scanf("%8d", &account_number);

Daniel Pereira
  • 1,785
  • 12
  • 10
  • 1
    This does not work. It looks for at most 8 digit input but not exactly 8 digits as required. – unxnut May 31 '13 at 02:45
  • I don't see where it is required to have an 8 digit mask, just 8 digits input. Speculating here, but this looks like a homework assignment so I think using is a mistake (though a good way to do this). BUT, you could just add the leading zeros to get an 8-digit account number in the format specifiers of the printf statement. – Daniel Pereira May 31 '13 at 02:49
  • 1
    I am sure it is a homework assignment but he has made an honest effort and is stuck. I believe it is an 8 digit mask because of the way he has structured his original code, checking for 8 digit input. – unxnut May 31 '13 at 02:55
  • If that's the case then I agree a string is appropriate. – Daniel Pereira May 31 '13 at 02:59