7

I am attempting to format a space-delimited user input for a programming assignment.

Essentially, the input consists of an arbitrary number of expressions

L integer integer integer integer and C integer integer integer.

For example: L 1 1 5 7 C 4 5 3.

So far, I've managed to extract the integers depending on the initial character, and can iterate through the string using the scanf function:

char a;
while(scanf("%c", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        scanf("%d %d %d", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}

Unfortunately, although this outputs the desired integers, the loop (and user input prompt) doesn't terminate.

Could someone please enlighten me as to why this is happening?

Mad Dog Tannen
  • 7,129
  • 5
  • 31
  • 55
Julian Laval
  • 1,210
  • 4
  • 17
  • 34
  • Do you feed an `EOF` (by Ctrl+D in Linux or Ctrl+Z in Windows) in the end of your input? – timrau Nov 11 '13 at 17:08
  • @timrau feeding an `EOF` ends the input whilst the program is running but I would expect it to terminate on `Enter` as well. – Julian Laval Nov 11 '13 at 17:19

4 Answers4

20

This is because \n is always there to make scanf("%c", &a) == 1 always true.
Change your

while(scanf("%c", &a) == 1) 

to

while(scanf(" %c", &a) == 1)  
     //      ^space before format specifier.  

A space before %c will eat up this \n left behind by scanf (on pressing Enter).

haccks
  • 104,019
  • 25
  • 176
  • 264
  • This doesn't seem to work; adding a space hasn't changed anything. Any ideas? – Julian Laval Nov 11 '13 at 17:21
  • @haccks I have written a program which reads some characters. `printf("\n 1. Give a character : \n");` `scanf(""%c", &ch);` `while(ch != '\0'){` `...` `printf("\n 2. Give a character : \n");` `scanf("%c", &ch);` `}` But running this program, the second printf is printed twice. After having added a space before `%c` , it worked correctly! Could you explain me why we have to add a spece before `%c`, so that the second printf is printed only once?? – Mary Star Nov 01 '14 at 13:36
  • 4
    The reason is simple which I explained in the above answer. When you press "Enter" key then a newline character `\n` is go to the input buffer. If `a` is the input character then input buffer will contain `a\n`. First `scanf` will read `a` leaving `\n` in the buffer for next call of `scanf`. On entering the loop, your second `scanf` will read this leftover `\n` from input buffer and loop runs one more time without waiting for your input and that's why you are getting `Give a character` line twice. – haccks Nov 01 '14 at 14:15
  • 1
    A space before %c will eat up this `\n` (or **any number of whitespaces**) left behind by `scanf`. – haccks Nov 01 '14 at 14:17
  • If I want to read `1 2\n`, I should use `scanf("%d %d ")`, i.e. appending a space for each "%d", correct? – NeoZoom.lua Feb 11 '21 at 06:00
  • 1
    Wow! @haccks you're amazing. I was facing the exact same issue and your post helped solve it. – Stats_Lover Jun 29 '22 at 19:02
4

The reason is scanf reads directly from the standard input and which blocks and waits for user input after it has processed the line. What you need to do is read the line and process that line in your while loop. I've modified your code below.

char a;
char line[1024];

fgets(line, 1023, stdin);   // leave 1 character for null terminator
while(sscanf(line, "%c", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        sscanf(line, "%d %d %d", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        sscanf(line, "%d %d %d %d", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}
T.V.
  • 505
  • 3
  • 10
2

Combining some features of other posts and some additions.
Use fgets() and %n inside sscanf(). Be sure to check results of sscanf().

char line[1024];
while (fgets(line, sizeof line, stdin) != NULL)) {
  char *s = line; 
  char Type;
  int n;
  while(sscanf(s, " %c%n", &Type, &n) == 1) {
    s += n;
    if(Type == 'C') {
      int inputX, inputY, inputR;
      if (3 != sscanf(s, "%d %d %d%n", &inputX, &inputY, &inputR, &n)) {
        Handle_Syntax_Error();
      }
      s += n;
      printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(Type == 'L') {
      int x1, y1, x2, y2;
      if (4 != sscanf(s, "%d %d %d %d%n", &x1, &y1, &x2, &y2, &n)) {
        Handle_Syntax_Error();
      }
      s += n;
      printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
    else {
      Handle_Syntax_Error();
    }
  }
} 
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1
char a;
    while(scanf("%[LC]", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        scanf("%d %d %d%*c", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        scanf("%d %d %d %d%*c", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}

"%[LC]" makes sure that only 'L' or 'C' will be scanned. After taking 3 or 4 of an integer input accordingly, both 'space' or 'enter' or 'any single character' will be discarded "%*c", after that, anything but 'L' or 'C' will break loop.

saint_sharan
  • 119
  • 1
  • 12