tm711
tm711

Reputation: 11

How do I make this repeat in C?

Just starting out and working through cs50. I want to:

  1. prompt the user to input a number
  2. count the number of digits in that number
  3. if the number of digits is NOT equal to 13, 15, or 16: print "INVALID"
  4. if "INVALID" repeat from step 1
#include <cs50.h>
#include <stdio.h>

int main(void)

{

    long n = 0;

    n = get_long("Number: ");

    int i = 0;

    long cn = n;

    while (cn > 0)
    {
        cn = cn / 10;
        i++;
    }
    
    if (i != 13 && i != 15 && i != 16)
    {
        printf("INVALID\n");
    }
}

This gets me through steps 1-3, but I cannot figure out how to get step 4 to work. Any help is greatly appreciated.

Upvotes: 1

Views: 250

Answers (4)

Fe2O3
Fe2O3

Reputation: 8354

OP: "I cannot figure out how to get step 4 [repeat] to work"

A: Simply wrap the code to be repeated inside a for(), a while() or a do/while() loop. Perhaps you missed a segment of the course material because loops are parts of the language used in almost every C program.


One has to be careful about making assumptions. The leading digit(s) may be 0, and the number value may not reflect what was entered.

#include <stdio.h>
#include <ctype.h> // for C library function 'isdigit()'
#include "cs50.h"

int main( void ) {
    for( ;; ) { // infinite loop to avoid tedium
        char *ps = get_string( "Enter a number: " );

        int cnt = 0;
        while( isdigit( (unsigned char)*ps ) ) {
            ps++;
            cnt++;
        }

        // positive logic may be easier for some readers
        // MUST also test that user's string entry has been exhausted
        if( *ps == '\0' && ( cnt == 13 || cnt == 15 || cnt == 16 ) )
            ; // all good
        else
            printf( "INVALID\n" );
    }

    return 0;
}

Output:

Enter a number: 1234567890123
Enter a number: 123456789012345
Enter a number: 1234567890123456
Enter a number: 12345678901234567
INVALID
Enter a number: 0000000000000
Enter a number: 0001112223334
Enter a number:

Use a "helper function"
An alternative version indicating how to continue processing with valid string of digits.

#include <stdio.h>
#include <ctype.h> // for library function 'isdigit()'
#include "cs50.h"

int isValidLen( char *ps ) { // return 0 for bad, 1 for good
    // THIS 'ps' is a local copy.
    // Same value, but not the same variable as in main().
    int cnt = 0;
    while( isdigit( (unsigned char)*ps ) ) {
        ps++;
        cnt++;
    }

    return *ps == '\0' && ( cnt == 13 || cnt == 15 || cnt == 16 );
}

int main( void ) {
    for( ;; ) {// infinite loop to avoid tedium
        char *ps = get_string( "Enter a number: " );

        if( !isValidLen( ps ) ) {
            printf( "INVALID\n" );
            continue; // or something else???
        }
        // main()'s copy of 'ps' still points to start of the string

        /*
         * perhaps more processing with validated string
         */
    }

    return 0;
}

Explore C's (reliable) Standard Library function
Or, one can make use of standard C library functions, instead...

#include <stdio.h>
#include <string.h> // for library function 'strspn()'
#include "cs50.h"

int main( void ) {
    for( ;; ) { // infinite loop to avoid tedium
        char *ps = get_string( "Enter a number: " );

        int cnt = strspn( ps, "0123456789" );

        if( ps[ cnt ] == '\0' && ( cnt == 13 || cnt == 15 || cnt == 16 ) )
            ; // all good
        else
            printf( "INVALID\n" );
    }

    return 0;
}

Knowing (now) that this CS50 assignment deals ONLY with 3 brands of credit cards, and that none of those brands' numbers begin with '0', using the provided library's get_long() is reasonable, but ONLY if the data is stored in a long long variable:

        long long ccn = get_long( "Enter a number: " );

        // Use a copy to "erase" digits from right-to-left until nothing remains
        int cnt = 0;
        for( long long cpy = ccn; cpy; cpy /= 10 )
            cnt++;

        if( cnt == 13 || cnt == 15 || cnt == 16 )
            ; // all good
        else
            printf( "INVALID\n" );

Left as an exercise for the beginner:

  • Does the CS50 get_long() return negative numbers? If so, should the program detect and reject those?
  • Some data entry functions accept numbers entered in octal or hexadecimal notation. Does the CS50 get_long() only accept decimal digits?
  • Learn the (approximate) maximum decimal value that can be stored in C's different "integer" datatypes (eg: char, short, int,... both as signed and unsigned values.) Hint: Some compilers treat long as only 32 bits. The maximum value of their unsigned long is just over 4 billion (i.e. not big enough to store 13, 15 or 16 decimal digit numbers.)

Upvotes: 6

Imran Mumtaz
Imran Mumtaz

Reputation: 39

Put the specific part in a while(1) loop. So here is the solution:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    long n = 0;
    int i = 0;

    long cn;
    while(1)
    {
        i=0;
         n = get_long("Number: ");
         cn = n;
        while (cn > 0)
        {
            cn = cn / 10;
            i++;
        }
       
        if (i != 13 && i != 15 && i != 16)
        {
            printf("INVALID\n");
        }
        else{
            break;
        }
    }
}

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84579

Even simpler, loop continaully and use the snprintf (NULL, 0, ...) trick to determine the number of digits. Break only if the number of digits is correct.

From man 3 snprintf

The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit, then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available.

#include <cs50.h>
#include <stdio.h>

int main(void) {
   
  long n = 0;
  
  for (;;) {            /* loop continually */
    int ndigits = 0;
  
    n = get_long("Number: ");
  
    ndigits = snprintf (NULL, 0, "%ld", n);
    
    if (n < 0) {        /* subtract 1 for '-' sign */
      ndigits -= 1;
    }
    
    if (ndigits == 13 || ndigits == 15 || ndigits == 16) {
      break;            /* break loop only if ndigits correct */
    }
    
    puts ("INVALID");
  }
}

(note: technically '-' isn't a digit, so you need to account for negative values)

Example Use/Output

./bin/ndigitstest
Number: 1
INVALID
Number: 123456789012
INVALID
Number: 1234567890123

Or, with negative values,

$ ./bin/ndigitstest
Number: -123456789012
INVALID
Number: -1234567890123

and so on...

Upvotes: 3

GAVD
GAVD

Reputation: 2134

Simplest way is that you give a flag to check input is invalid or not. For example,

int invalid = 1;
long n = 0;

while (invalid) {
    n = get_long("Number: ");
    int i = 0;
    long cn = n;

    while (cn > 0)
    {
        cn = cn / 10;
        i++;
    }

    if (i != 13 && i != 15 && i != 16)
    {
        printf("INVALID\n");
    } else {
        invalid = 0;
    }

}

Upvotes: 2

Related Questions