1

I am writing a program to read a user input statement and extract all integers from the input. For example, if I enter "h3ll0", the program will output "30". I have used the fgets function to read the user input.

However, I am currently reading about getchar() and would like to know what would be the best way to use getchar() in my program to read user input instead of fgets. I am not really clear on how getchar() works and what situations it can be useful in.

This question is related to a project that specifically asks for getchar() as the method of reading user input. As I was unclear on how getchar() works, I built the rest of the program using fgets to ensure it was working.

#include <stdio.h>

int main()
{
    char user_input[100];
    int i;
    int j = 0;

    printf("Please enter your string: ");
    fgets(user_input ,100, stdin);

    for(i = 0; user_input[i] ; i++)
    {

        if(user_input[i] >= '0' && user_input[i] <= '9')
        {
            user_input[j] = user_input[i];
            j++;
        }
    }


    user_input[j] = '\0';

    printf("Your output of only integers is: ");
    printf("%s\n", user_input);

    return 0;
}
  • 1
    You mean "use getchar()" instead of "implement getchar()", don't you? – Yunnosch Sep 14 '19 at 07:40
  • 1
    Stay with `fgets()` to spare yourself a lot of hassle and problems. – Yunnosch Sep 14 '19 at 07:41
  • I think however that you should only read 99 characters into your 100 char array, to have space for the `'\0'` at the end. – Yunnosch Sep 14 '19 at 07:43
  • You are aware that you can [edit] your question to add info and fix potential misunderstandings, aren't you? – Yunnosch Sep 14 '19 at 07:47
  • If you have to use `getchar()` when you have a working code otherwise, then please have a look at https://meta.stackoverflow.com/questions/334822/how-do-i-ask-and-answer-homework-questions Because it seems a typical teacher-requirement. – Yunnosch Sep 14 '19 at 07:47
  • @Yunnosch There are far more hassles with fgets. This problem (as most) is much easier with getchar than fgets. – William Pursell Sep 14 '19 at 11:56
  • @WilliamPursell I look at the comment by chux on an answer below and do not really feel wrong about my comment... – Yunnosch Sep 14 '19 at 11:59

3 Answers3

0

Something like this should work as a clunky replacement to fgets using only getchar. I don't guarantee the accuracy of the error handling.

I don't think you would ever want to use getchar over fgets in an application. Getchar is more limited and less secure.

#include <stdint.h>

void    your_fgets(char *buffer, size_t buffer_size)
{
    int     i;
    size_t  j;

    if (buffer_size == 0)
        return ;
    else if (buffer_size == 1)
    {
        buffer[0] = '\0';
        return ;
    }
    j = 0;
    while ((i = getchar()) != EOF)
    {
        buffer[j++] = i;
        if (j == buffer_size - 1 || i == '\n')
        {
            buffer[j] = '\0';
            return ;
        }
    }
    buffer[j] = '\0';
}
AugustinLopez
  • 348
  • 1
  • 3
  • 13
0

OP: unclear on how getchar() works

int fgetc(FILE *stream) typically returns 1 of 257 different values.

"If ... a next character is present, the fgetc function obtains that character as an unsigned char converted to an int C11 §7.21.7.1 2

On end-of-file or input error (rare), EOF, is returned.


OP: to use getchar() in my program to read user input instead of fgets.

Create your own my_fgets() with the same function signature and same function as fgets() and then replace.

char *fgets(char * restrict s, int n, FILE * restrict stream);

The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array. C11 §7.21.7.2 2

Return the same value

The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned. If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned. §7.21.7.2 3


Sample untested code

#include <stdbool.h>
#include <stdio.h>

char *my_fgets(char * restrict s, int n, FILE * restrict stream) {
  bool something_read = false;
  int ch = 0;
  char *dest = s;

  // Room ("reads at most one less") and EOF not returned?
  while (n > 1 && (ch = fgetc(stream)) != EOF) {
    n--;
    something_read = true;
    *dest++ = (char) ch;
    if (ch == '\n') {
      break;  // "No additional characters are read after a new-line character"
    }
  }

  // Did code end the while loop due to EOF?
  if (ch == EOF) {
    // Was EOF due to end-of-file or rare input error?
    if (feof(stream)) {
      // "If end-of-file is encountered and no characters ... read into the array ..."
      if (!something_read) {
        return NULL;
      }
    } else {
      // "If a read error ..."
      return NULL;  // ** Note 1
    }
  }

  // room for \0?
  if (n > 0) {
    *dest = '\0';  //" A null character is written immediately after the last character"
  }

  return s;
}

Perhaps improve fgets() and use size_t for n.

char *my_fgets(char * restrict s, size_t n, FILE * restrict stream);

fgets() with n <= 0 is not clearly defined. Using size_t, an unsigned type, at least eliminates n < 0 concerns.

Note 1: or use s = NULL; instead of return NULL; and let the remaining code null terminate the buffer. We have that option as "array contents are indeterminate".

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

I am baffled by the comments on this post suggesting that fgets is easier to use. Using fgets unnecessarily complicates the issue. Just do:

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

int
main(int argc, char **argv)
{
        int c;
        while( ( c = getchar() ) != EOF ) {
                if(isdigit(c) && (putchar(c) == EOF)) {
                        perror("stdout");
                        return EXIT_FAILURE;
                }
        }
        return ferror(stdin);
}

There is absolutely no reason to use any additional buffering, or read the input one line at a time. Maybe you'll want to output newlines as they come in, but that would be an implementation detail that is left unspecified in the question. Either way, it's utterly trivial (if(( c == '\n' || isdigit(c)) && (putchar(c) == EOF))). Just read a character and decide if you want to output it. The logic is much easier if you don't think about the input as being more complicated than it is. (It's not line-oriented...it's just a stream of bytes.)

If, for some unknown reason you want to make this tool usable only in an interactive setting and load up your output with excess verbosity, you can easily do something like:

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


int
main(int argc, char **argv)
{
        int c;
        do {
                int want_header = 1;
                printf("Please enter your string: ");

                while( ( c = getchar() ) != EOF && c != '\n' ) {
                        if(! isdigit(c)) {
                                continue;
                        }
                        if(want_header) {
                                want_header=0;
                                printf("Your output of only integers is: ");
                        }
                        if(putchar(c) == EOF) {
                                perror("stdout");
                                return EXIT_FAILURE;
                        }
                }
                if( c == '\n')
                        putchar(c);
                want_header = 0;
        } while(c == '\n');
        return ferror(stdin);
}

but, please, don't do that. (Imagine if grep started by emitting a prompt that said "please enter the regex you would like to search for"!)

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 1
    To un-baffle: Directly using `getchar()` without a buffer is a good idea as the coding goal really has no buffering requirement, especially is we assume "extract all integers" was really "extract all _digits_". OTOH, this is an interesting case where OP purportedly has working code and wants to replace `fgets()` with `getchar()` -_somehow_. Re-factoring overall code, as you suggest is good here, yet in larger codes it is safer to limit the change and locality. With a `fgets()` replacement that is accomplished with less risk. – chux - Reinstate Monica Sep 14 '19 at 12:35
  • Notice that the additional logic for the completely unnecessary and undesirable verbosity has introduced an error: if the attempt to write a newline fails, we are not catching the error. Convoluted logic is bad. Avoid it. – William Pursell Sep 14 '19 at 12:35
  • This works well if you just want to output the result, and a fine example where *getchar* is more adapted that *fgets*. We do not know why OP wants to use *getchar* rather than *fgets*, and losing the buffer might be a problem for further processing. – AugustinLopez Sep 14 '19 at 12:37
  • And we're not checking *any* of the printfs for errors. Ick. – William Pursell Sep 14 '19 at 12:54
  • Note: it may seem like there's no need to check printf for errors, but it is *not* an esoteric error. The usually work flow is that code does not check for errors, a process writes data to a pipe that is closed, the process receives a SIGPIPE and terminates and all is good. But, if the process is run in a python subprocess which has passed down a disposition to ignore SIGPIPE, then the process does not terminate. Always check for write errors. – William Pursell Sep 14 '19 at 13:21