Sunday, March 30, 2025

How to correctly read number in C

 Naive and wrong approach which will crash if user inputs anything but number:

#include <stdlib.h>
#include <stdio.h>

static int read_number(void) {
    int number;
    scanf("%d", &number);
    return number;
}

Handcrafted character by character solution:

#include <stdio.h>
#include <stdlib.h>

static int read_number(const char *err_prompt, const int err_num) {
    while (1) {
        int number = 0, neg = 0, c = getchar();
        while (c == ' ') c = getchar();     // skip leading spaces
        if ((c == '+') || (c == '-')) {
            neg = (c == '-');
            c = getchar();
        }
        while ((c >= '0') && (c <= '9')) {
            number = (10 * number) + c - '0';
            c = getchar();
            if (c == '\r') c = getchar();   // DOS/Windows EOL
            if (c == '\n') return neg ? -number : number;
        }
        while (c != '\n')                   // read rest of line
            if (c == EOF) return err_num;
            else c = getchar();
        printf("%s", err_prompt);
    }
}

Version which uses standard libarary functions:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int read_number(const char *err_prompt, const int err_num) {
    char *line = NULL;
    size_t len = 0;
    int number = err_num;
    while (1) {
        if (getline(&line, &len, stdin) == -1) break;   // EOF or error
        line[strcspn(line, "\r\n")] = 0;
        char *endptr;
        number = strtol(line, &endptr, 10);
        if ((*line != 0) && (*endptr == 0)) break;
        printf("%s", err_prompt);
        number = err_num;
    }
    free(line);
    return number;
}