0

Im trying to parse an input string in the format x y z (spaces included) so that it will exit the loop when the entered string is valid. However, when I run this code, it outputs "incorrect format" 3 times and each time, valid = 1. Is there any other way to get this to work without having to parse it manually?

int valid = 0, x, y, z;
char str[20];
while (valid != 3) {
  scanf("%s", str);
  valid = sscanf(str, "%d %d %d", &x, &y, &z);
  if (valid != 3) {
    printf("Incorrect format\n");
  }
}
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Orcinuss
  • 39
  • 1
  • 1
  • 7
  • 1
    The `%s` format specifier will only read up to the first *whitespace*. You simply want `if (scanf ("%d %d %d", &x, &y, &z) != 3) { fprintf (stderr, "error: incorrect format.\n"); return 1; }` You don't need `str` you don't need `valid` and you don't need a `while` loop. – David C. Rankin Nov 27 '17 at 00:45
  • 2
    @DavidC.Rankin If the input is line-oriented then it is a good idea to read a whole line and then use `sscanf` on it. For example your suggestion won't deal with the input string `123` followed by Enter , it will keep reading more lines – M.M Nov 27 '17 at 00:54
  • I agree whole heartedly, but with the loop and no example input it was ambiguous. – David C. Rankin Nov 27 '17 at 00:59
  • this line: `scanf("%s", str);` should be: `if( 1 != scanf("%19s", str) ) { fprintf( stderr, "scanf for input line failed" ); } // implied else, scanf successful` – user3629249 Nov 27 '17 at 11:10
  • 1
    this statement: `char str[20];` is not allowing near enough room. Suggest something more like: `char str[50];` And then the call to `scanf()` would be: `if( 1 != scanf("%49[^\n]", str) ) { fprintf( stderr, "scanf for input line failed" ); } // implied else, scanf successful` Use `"%49[%\n]` because this will read all the way to a newline -or- 49 characters, which ever comes first. The input specifier '%49s' will stop inputting at the first space. – user3629249 Nov 27 '17 at 11:18
  • A better option for reading a line would be to use either `get_s()`, `fgets()`, or `getline()` instead. – Remy Lebeau Mar 27 '23 at 22:37

2 Answers2

4

In your code, the primary stumbling block you create is to attempt a read with scanf ("%s", str). The %s format specifier will only read characters up to the first whitespace encountered. If you enter 3-integers separated by a space, you will never read more than the first one into str. This makes it impossible to parse 3-integer values with sscanf from str that can contain no more than 1.

To correct the problem, either read 3-integer values directly with scanf, e.g.

#include <stdio.h>

int main (void) {
    
    int x, y, z;
    printf ("enter 3 integers: ");
    if (scanf ("%d %d %d", &x, &y, &z) != 3) {
        fprintf (stderr, "error: invalid input.\n");
        return 1;
    }
    printf ("x : %d\ny : %d\nz : %d\n", x, y, z);
    
    return 0;
}

Or, the preferred way to handle line-oriented input is by using the line-oriented input functions fgets or POSIX getline. This will avoid the many pitfalls with taking user input with scanf that new C programmers routinely fall into. Never, never, never use gets, it is so insecure it has been removed from the C11 library.

An equivalent example using fgets and sscanf would be:

#include <stdio.h>

#define MAXC 128

int main (void) {
    
    int x, y, z;
    char buf[MAXC] = "";
    printf ("enter 3 integers (on a single line): ");
    if (fgets (buf, MAXC, stdin) == NULL) {
        fprintf (stderr, "error: user canceled input.\n");
        return 1;
    }
    if (sscanf (buf, "%d %d %d", &x, &y, &z) != 3) {
        fprintf (stderr, "error: invalid input.\n");
        return 1;
    }
    printf ("x : %d\ny : %d\nz : %d\n", x, y, z);
    
    return 0;
}

Example Use/Output

(it is the same for both)

$ ./bin/rd3int
enter 3 integers: 10 12 14
x : 10
y : 12
z : 14

Look things over and let me know if you have further questions.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 128 is certainly better than OP's 20, but I usually like an obnoxiously generous input buffer size, like `char buf[4096]`, to guard against the user falling asleep on the `z` key, or in this case playing bongos on the number keys and space bar ;) – ad absurdum Nov 27 '17 at 01:30
  • Agreed. Here since we were looking at no more than 3 int values (max 10 chars each + 2 spaces) -- I just quadrupled it `:)`. – David C. Rankin Nov 27 '17 at 01:37
0

scanf("%s", str) scans text until space occurs. So for input 1 2 3, str is read 3 times, with content 1, 2, 3.

Try using gets(str) instead.

delta
  • 3,778
  • 15
  • 22
  • 2
    Oh no. [Never ever use the unsafe, deprecated in C99 and eradicated from the Standard in C11, `gets()` function.](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) Instead, use `fgets()` or the POSIX `getline()` function. You can even use the much-maligned `scanf()` more safely than `gets()`. – ad absurdum Nov 27 '17 at 00:45