简体   繁体   中英

Ways for reading inputs in C without reading newline or using scanf

I have a college exercise to do, that consists in writing a bank in C and I do know that scanf is a really buggy function and fgets reads the '\n' , that I don't really want to be read. So I have written new functions trying to solve this problem:

char *readstr(char *buff, size_t bytes) {
    char *ptr;
    for (ptr = buff;
         (*ptr = getchar()) != '\n' && *ptr != EOF && ptr - buff < bytes - 1;
         ptr++)
        ;
    int c = *ptr;
    *ptr = '\0';
    if (c != EOF && c != '\n') {
        while ((c = getchar()) != '\n' && c != EOF)
            ;
    }
    return buff;
}

int readint() {
    char buffer[256] = "";
    readstr(buffer, 256);
    return atoi(strpbrk(buffer, "0123456789"));
}

double readfloat() {
  char buffer[256] = "";
  readstr(buffer, 256);
  return atof(strpbrk(buffer, "0123456789"));
}

char readchar() {
  char buffer[2] = "";
  readstr(buffer, 2);
  return *buffer;
}

until now, I wrote these ones. Any advice or suggestion? a more elegant one or simpler solution? apparently they work, but I don't know if this is the best approach.

scanf is not buggy, it's simply hard to use correctly.

Anyway, you can use fgets and remove manually the newline character. A way to do this is by using the strcspn function like follows:

fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;

Your goal seems to read a line of input into a buffer with a given size, not storing the trailing newline and discarding any excess characters present on the line. You do need to read the newline so it does not linger in the input stream, but you do not want to store it into the destination array as fgets() does.

Note that this can be achieved with scanf() this way:'

char buf[100] = "";
if (scanf("%99[^\n]", buf) == EOF) {
    // handle end of file
} else {
    // line was read into buf or empty line was detected and buf was not modified
    scanf("%*[^\n]");  // consume extra bytes on the line if any
    scanf("%*1[\n]");  // consume the newline if present
}

This is very cumbersome, and it gets even worse if the buffer size is a variable value. As many savvy C programmers noted, scanf is not buggy , it is just difficult to use correctly. I would even say its semantics are confusing and error prone, yet it can be used safely, unlike gets() , which was removed from recent versions of the C Standard because any arbitrary long input can cause undefined behavior with it.

Reading a line and discarding the trailing newline can be done with fgets() , but combining the reading the line, discarding extra bytes and consuming the newline may be useful as a separate function.

There are problems in your code:

  • you store the return value of getchar() directly into the char array, removing the distinction between EOF and the char value '\377' on architectures with signed chars, while *ptr != EOF would never match on architectures where char is unsigned by default. You must store it into an int variable.

  • there is no way to tell if the end of file was reached: the function returns an empty string at end of file, just like it does for blank lines int the stream

  • the buffer size cannot be zero.

  • truncation cannot be detected: instead of returning the destination array, you could return the number of characters needed for the full line and -1 at end of file.

  • calling atoi(strpbrk(buffer, "0123456789")) poses multiple problems: strpbrk can return a null pointer, causing undefined behavior, it will skip a leading sign, and atoi is not fully defined for contents representing values out of range for type int .

Here is a modified version:

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

int readstr(char *buff, size_t bytes) {
    size_t pos = 0;
    int c;
    while ((c = getchar()) != EOF && c != '\n') {
        if (pos + 1 < bytes)
            buff[pos] = (char)c;
        pos++;
    }
    if (size > 0) {
        if (pos < size)
            buff[pos] = '\0';
        else
            buff[size - 1] = '\0';
    }
    if (c == EOF && pos == 0)
        return -1;
    return (int)pos;
}

int readint(void) {
    char buffer[256];
    long n;
    if (readstr(buffer, sizeof buffer) < 0)
        return -1;
    n = strtol(buffer, NULL, 0);
#if LONG_MIN < INT_MIN
    if (n < INT_MIN) {
        errno = ERANGE;
        return INT_MIN;
    }
#endif
#if LONG_MAX > INT_MAX
    if (n > INT_MAX) {
        errno = ERANGE;
        return INT_MAX;
    }
#endif
    return (int)n;
}

double readdouble(void) {
    char buffer[256];
    if (readstr(buffer, sizeof buffer) < 0)
        return NAN;
    else
        return strtod(buffer, NULL);
}

int readchar(void) {
    char buffer[2] = "";
    if (readstr(buffer, sizeof buffer) < 0)
        return EOF;
    else
        return (unsigned char)*buffer;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM