0
void someFunction(){
    char *buffer;
    size_t bufsize = 32;
    int bytes_read;

    for (;;) {
        buffer = (char *) malloc(bufsize * sizeof(char));
        if (buffer == NULL) {
            perror("Unable to allocate buffer");
            exit(1);
        }

        FILE *ptr;
        ptr = fopen("sample.txt", "a");
        printf("Enter Stuff to write down:\n");
        //getline(&buffer,&bufsize,stdin);
        //fgets(buffer, 30, stdin);
        //scanf("%[^\n]%*c", buffer);
        //scanf("%s", buffer);

        if (buffer[0] == '0') {
            break;
        }

        WriteWithFprintf(ptr, buffer);
        free(buffer);
        fclose(ptr);
    }
}

The problem is: if I use

    getline(&buffer,&bufsize,stdin);

or

    fgets(buffer, 30, stdin);

then it escapes the first like so:

    Enter Stuff to write down:
    Enter Stuff to write down:
    0

If I use:

    scanf("%[^\n]%*c", buffer);

then I get an infinite loop.

It does work with:

   scanf("%s", buffer);

but I want input with space so this is not an option for me.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Aside: instead of this nasty (and ineffective) kludge `scanf("%[^\n]%*c", buffer);` use `scanf(" %[^\n]", buffer);` Notice the added space. – Weather Vane Aug 28 '22 at 13:01
  • 1
    Those two are not equivalent, @WeatherVane. – John Bollinger Aug 28 '22 at 13:02
  • Using `getline` or `fgets` you will never reach the `break` as both include the `\n` character. – Gerhardh Aug 28 '22 at 13:02
  • @JohnBollinger I know. But because of the lack of a space at the beginning, that kludge would need to be applied to all `scanf` lines, no matter what the specifiers. The `scanf` is designed to filter whitespace, when used correctly. – Weather Vane Aug 28 '22 at 13:03
  • 2
    Do you use `scanf` somewhere before you call that function? Please show a minimal complete program that reproduces the issue. A common issue when mixing `scanf` and `fgets` is [scanf() leaves the newline character in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-newline-character-in-the-buffer) – Gerhardh Aug 28 '22 at 13:03
  • Anuj Borah, prior unposted code used `scanf()` in such a way the a `'\n'` was left in `stdin`. – chux - Reinstate Monica Aug 28 '22 at 13:24
  • 1
    @WeatherVane, your suggestion will consume not just one leading newline, but any amount of leading whitespace. It appears that that would not be a problem for the specific input the OP has / expects, but it should not go unremarked. Unlike the OP's variation, yours will also leave a trailing newline unread if there is one. That should not go unremarked either. – John Bollinger Aug 28 '22 at 13:31
  • @JohnBollinger the other format specifiers also leave a newline in the buffer. By removing it here, the OP has added yet another difference of the behaviour of `%[]` from other specifiers. They all leave unprocessed input in the buffer. I still say, it's an ill-advised solution. – Weather Vane Aug 28 '22 at 13:33
  • @WeatherVane, the OP's `"%[^\n]%*c"` *does not* leave a trailing newline in the buffer, provided that the `%[` matches anything and there are not two trailing newlines in a row. That is presumably the intent of the `%*c`, and certainly is its effect. – John Bollinger Aug 28 '22 at 13:35
  • @JohnBollinger obvoiusly that is the point, but you have a few *provideds* in there. I am saying *every* format specifier leaves the unprocessed input in the buffer (which might not be wanted), so why should you make an exception here? As mentioned, if you are not going to use a leading space with `%c` and `%[]` then this kludge is needed everywhere else too. IMO its a terrible solution to remove trailing whitespace like this. A kludge. – Weather Vane Aug 28 '22 at 13:38
  • @WeatherVane, the point is that you have suggested an alternative that has different behavior in this regard, without saying anything about the difference. And inasmuch as the OP is raising the `scanf` as an alternative to `getline()` and `fgets()`, it is sensible to choose a variation that indeed does consume a trailing newline. – John Bollinger Aug 28 '22 at 13:42
  • @Anuj Borah. What user input do you expect leads to `buffer[0] == '0'` to be true? – chux - Reinstate Monica Aug 28 '22 at 13:47
  • 2
    @WeatherVane Suggesting `scanf(" %[^\n]", buffer);`, without a _width_ is like suggesting evil `gets()`. Rather than solve one problem and pose another, better fix both: `scanf(" %31[^\n]", buffer);`. – chux - Reinstate Monica Aug 28 '22 at 13:52
  • 1
    @WeatherVane works as charm thanks – Anuj Borah Aug 28 '22 at 17:01
  • @chux-ReinstateMonica canf(" %31[^\n]", buffer); too work as charm thansk – Anuj Borah Aug 28 '22 at 17:04

2 Answers2

1

All of the behaviors described for different variations on your code are consistent with the next character available to be read from stdin being a newline, presumably from a preceding line of input.

In that case,

  • the getline() and fgets() alternatives will read the newline (and any preceding characters) as a line, and then loop to read the line you actually want on the second pass.
  • the first scanf() variation will read nothing on account of a matching failure for the %[^\n] field (leading whitespace is not skipped for %[ directives). Not having matched anything to that, there will be no attempt to match anything to the %*c.
  • the second scanf() alternative will work as you describe, because scanf will automatically consume leading whitespace when processing a %s directive, including any newline.

There is a variety of things you could do, depending on exactly how want to handle input. Here is one:

    int c = fgetc(stdin);
    if (c == EOF) {
        // handle eof ...
    } else if (c != '\n') {
        ungetc(c, stdin);
    }
    // your choice for reading the wanted data ...

That will consume up to one leading newline from stdin to get it out of your way.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    Well yes, @chux, *in that case* `scanf` will consume leading whitespace. The previous point establishes that `scanf` does not *always* do so. But I have edited to clarify. – John Bollinger Aug 28 '22 at 13:33
1

getline and fgets failed to get input on first attempt

Neither failed. Both simply read a '\n' and immediately returned. This '\n' was left-over from a previous input function like scanf("%s", ...), that did not consume the entire line. getline() is not part of the standard C library.

scanf("%[^\n]%*c", buffer); fails to read anything when the first available character is '\n'.

scanf("%s", buffer); consumes all optional leading white space like '\n'. This may appear to work for OP.

scanf("%[^\n]%*c", buffer); and scanf("%s", buffer); are both poor code as they do not have a width limit, risking buffer overflow.


I recommend for a learner to not use scanf() at all and perform all input with fgets(), including the part of code not posted.

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