Check if all values entered into char array are numerical

There are a couple of ways to do this.

The simple way is to use fgets to get a string.

Then, we can use strtol to decode the number [and we check the ending char for validity].

To do it completely manually, we can use isdigit in a loop, building up the number one digit at a time.

Here’s some example code that is annotated and shows both ways:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <termios.h>

// getstr -- get a string with prompt
// RETURNS: length or (<0 -> error)
int
getstr(char *buf,int buflen,const char *prompt)
{
    char *cp;
    int ret = 0;

    // decide if stdin is:
    // (1) a TTY
    // (2) a [redirected] file (e.g. invoked with ./myprogram < input)
    static int echoflg = -1;
    if (echoflg < 0) {
        struct termios tio;
        echoflg = (tcgetattr(fileno(stdin),&tio) < 0);
    }

    // NOTE: usage of the error codes in errno.h is arbitrary

    while (ret <= 0) {
        // ensure buffer has enough space
        if (buflen < 2) {
            ret = -ENOMEM;
            break;
        }

        // output prompt
        printf("%s: ",prompt);
        fflush(stdout);

        // get a line
        cp = fgets(buf,buflen,stdin);

        // EOF
        if (cp == NULL) {
            ret = -ENODATA;
            break;
        }

        // echo file input to simulate TTY input
        if (echoflg)
            fputs(buf,stdout);

        // get buffer length
        ret = strlen(buf);

        // empty string
        if (ret <= 0)
            continue;

        // point to last char
        cp = &buf[ret - 1];

        // ensure we got a newline -- if not, fgets had to chop the line (i.e.)
        // the line is too long to fit in the buffer
        if (*cp != '\n') {
            ret = -ENOSPC;
            break;
        }

        // strip the newline -- we are done
        *cp = 0;
        --ret;
    }

    return ret;
}

// getnum_strtol -- get number using strtol
long
getnum_strtol(const char *prompt)
{
    int len;
    int readflg = 1;
    char *cp;
    char buf[100];
    long num = 0;

    while (readflg) {
        len = getstr(buf,sizeof(buf),prompt);

        if (len < 0)
            exit(1);

        num = strtol(buf,&cp,10);

        // ensure we got a least one digit
        if (cp <= buf)
            continue;

        switch (*cp) {
        case ' ':
        case '\t':
        case 0:
            readflg = 0;
            break;
        default:
            printf("getnum_strtol: not a valid number -- buffer '%s', invalid '%s'\n",
                buf,cp);
            break;
        }
    }

    return num;
}

// getnum_manual -- get number _not_ using strtol
long
getnum_manual(const char *prompt)
{
    int len;
    int readflg = 1;
    int sign = 0;
    int valid;
    int chr;
    char *cp;
    char buf[100];
    long num = 0;

    while (readflg) {
        len = getstr(buf,sizeof(buf),prompt);

        // fatal error
        if (len < 0)
            exit(1);

        // point to buffer start
        cp = buf;

        // find first non-whitespace character
        valid = 0;
        while (1) {
            chr = *cp;

            // end of string
            if (chr == 0)
                break;

            // found character
            valid = ((chr != ' ') && (chr != '\t'));
            if (valid)
                break;

            ++cp;
        }
        if (!valid)
            continue;

        // reset the accumlated number and the sign
        num = 0;
        sign = 0;
        valid = 0;

        // loop through all characters in buffer
        while (1) {
            chr = *cp++;

            // get the sign of the number (and skip an explicit sign)
            if (sign == 0) {
                switch (chr) {
                case '+':
                    sign = 1;
                    chr = *cp++;
                    break;
                case '-':
                    sign = -1;
                    chr = *cp++;
                    break;
                default:
                    sign = 1;
                    break;
                }
            }

            // stop decoding number on whitespace
            switch (chr) {
            case ' ':
            case '\t':
                chr = 0;
                break;
            }

            // check for clean end of number
            if (chr == 0) {
                if (valid) {
                    readflg = 0;
                    break;
                }
            }

            // not a valid digit
            if (!isdigit((unsigned char) chr)) {
                cp -= 1;
                printf("getnum_manual: not a valid number -- buffer '%s', invalid '%s'\n",
                    buf,cp);
                break;
            }

            // add digit to number
            num *= 10;
            chr -= '0';
            num += chr;

            // we got at least one valid digit
            valid = 1;
        }
    }

    // apply sign
    num *= sign;

    return num;
}

int
main(int argc,char **argv)
{
    char *cp;
    int opt_s = 0;
    long num;

    // skip over program name
    --argc;
    ++argv;

    // get options
    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 's':                       // use strtol
            opt_s = !opt_s;
            break;
        }
    }

    while (1) {
        if (opt_s)
            num = getnum_strtol("Enter number [strtol]");
        else
            num = getnum_manual("Enter number [manual]");
        printf("The number entered is: %ld\n",num);
        if (num == 999)
            break;
    }

    return 0;
}

Edit: Added code to replay/echo input from file.

Leave a Comment