0

I am a C beginner trying to understand some nuances of scanf. This seems really trivial but I am struggling with reading inputs from stdin in "the correct way".

When terminal input is like string1 string3 string3 and I hit return, It works correctly and gives 3 in the result.

But when I give input like string1 and I hit return, I want the program to return 1 in the result variable and break the loop. Which doesn't happen. The program just expects me to enter more input into the terminal.

#include <stdio.h>
#define nameBufferLen 20

int main () {
    int result;
    char name[nameBufferLen];
    char opens[nameBufferLen]; 
    char closes[nameBufferLen];
    
    while(1) {
        result = fscanf(stdin,"%s %s %[^\n]s", name, opens, closes);
        printf("%s|%s|%s|  AND Result len is : %d\n", name, opens, closes, result);
        if (result!=3) {
            break;
        }
    }

    return 0;
}

I am curious to know what could be the approach and regex that enables me to do this with scanf.

bad programmer
  • 818
  • 7
  • 12
  • 1
    You can't do this with `scanf()`. Use `fgets()` to read a line, then use `sscanf()` to parse it. – Barmar Nov 08 '22 at 05:34
  • Also, your variables are declared wrong. They should be `char`, not `char *`. `char *name[nameBufferLen]` is an array of pointers, not a character string. – Barmar Nov 08 '22 at 05:34
  • @Barmar Apologies for the typo. I corrected it. But if possible , could you provide code snippet for doing so. It would mean a lot. thanks. – bad programmer Nov 08 '22 at 06:07
  • 1
    See [Using `sscanf()` in a loop](https://stackoverflow.com/q/3975236/15168) for how to process the line of input read by `fgets()` or POSIX `getline()`. The plain [`scanf()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html) function will skip white space, including newlines, before almost every conversion specification — the exceptions being `%c`, `%[…]` (scan sets) and `%n`. Note, too, that `%[…]` is not a modifier for `%s` but a completely separate specification. Using `%[^\n]s` is invariably wrong. – Jonathan Leffler Nov 08 '22 at 06:42

1 Answers1

2

Here is the implementation that @Barmar mentioned:

#include <stdio.h>
#define nameBufferLen 19
#define str(s) str2(s)
#define str2(s) #s

int main (void) {
    for(;;) {
        char s[3 * (nameBufferLen + 1)];
        if(!fgets(s, sizeof(s), stdin)) {
            printf("err\n");
            return 1;
        }
        char name[nameBufferLen+1] = { 0 };
        char opens[nameBufferLen+1] = { 0 };
        char closes[nameBufferLen+1] = { 0 };
        int result = sscanf(s,
            "%" str(nameBufferLen) "s"
            "%" str(nameBufferLen) "s"
            "%" str(nameBufferLen) "s",
            name, opens, closes
        );
        if(result == 1)
            break;
    }
}

and example run:

a b c
a b
a

If you enter strings longer than 19 the excess will spill into the next variable. You can detect that with strlen() of each of the variables. Alternatively you can parse the string s with, for example, strpbrk().

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • 1
    There's no obvious reason not to use `sizeof(s)` as the second argument to `fgets()`. The `sscanf()` call should use `"%19s %19s %19s"` to avoid "`a b absolutely-horrendously-long-verbiage`" from overflowing the arrays. That's only 42 characters long, but the `closes` array would be seriously overflowed by the input. And yes, it is an utter PITA that you have to specify the size 'off by one' and literally in the format string. – Jonathan Leffler Nov 08 '22 at 07:02
  • 1
    @JonathanLeffler Thanks. I fixed the first issue, and prefer a constant instead of magic values even though it's a pita to look at. – Allan Wind Nov 08 '22 at 07:06