4

I'm developing a chess game in C just for practicing. At the beginning of the game, the user can type 4 things:

  • ROW<whitespace>COL (i.e. 2 2)
  • 'h' for help
  • 'q' to quit

How can I use a scanf to expect 2 integers or 1 char?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Rodrigo Souza
  • 7,162
  • 12
  • 41
  • 72

5 Answers5

6

Seems like it would be most sensible to read a whole line, and then decide what it contains. This will not include using scanf, since it would consume the contents stdin stream.

Try something like this :

char input[128] = {0};
unsigned int row, col;
if(fgets(input, sizeof(input), stdin))
{
    if(input[0] == 'h' && input[1] == '\n' && input[2] == '\0')
    {
        // help
    }
    else if(input[0] == 'q' && input[1] == '\n' && input[2] == '\0')
    {
        // quit
    }
    else if((sscanf(input, "%u %u\n", &row, &col) == 2))
    {
        // row and column
    }
    else
    {
        // error
    }
}
Daniel Kamil Kozar
  • 18,476
  • 5
  • 50
  • 64
  • 2
    In the case you read just 1 byte from stdin, you are then accessing uninitialized variables and you're in UB. Quick solution for this is just to initialize the input array – Zaffy Aug 20 '13 at 08:03
5

It's better to avoid using scanf at all. It usually causes more trouble than what it solves.

One possible solution is to use fgets to get the whole line and then use strcmp to see if the user typed 'h' or 'q'. If not, use sscanf to get row and column.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 1
    @RodrigoAlves Go search how to use functions `fgets`, `sscanf`, etc. Make some toy programs using them. I'm sure it doesn't take much effort for you to master them. – Yu Hao Aug 20 '13 at 07:27
  • `fgets` can't get the whole line in one call reliably. Of course, in this case it's not an issue, since we don't need more than 3 characters anyway, but in different situation it could be a problem. – Joker_vD Aug 20 '13 at 07:29
  • @Joker_vD : What's stopping you from calling `fgets` and expanding your buffer until you hit a newline? – Daniel Kamil Kozar Aug 20 '13 at 07:32
  • @Joker_vD If you mean `fgets` may only get part of the line, because it has be specify the maximum characters to read, that's actually a good thing, comparing to `gets`, `fgets` won't cause buffer overflow. But you are right to be careful with this fact. – Yu Hao Aug 20 '13 at 07:34
  • @YuHao Congrats on your 10K :) You are one rapidly scorer! – Grijesh Chauhan Aug 20 '13 at 11:22
  • @GrijeshChauhan Thanks. Hopefully I'll keep active, I really enjoyed the community of SO. – Yu Hao Aug 20 '13 at 12:58
0

This one is just using scanf

#include <stdio.h>
int main()
{
        char c;
        int row, col;
        scanf("%c", &c);
        if (c == 'h')
                return 0;
        if (c == 'q')
                return 0;
        if (isdigit(c)) {
                row = c - '0';
                scanf("%d", &col);
                printf("row %d col %d", row, col);
        }
        return 0;
}
Sakthi Kumar
  • 3,047
  • 15
  • 28
  • @DanielKamilKozar This code looks like it should work. It should accept two digits separated by a space. Remember scanf reads per format statement, it is NOT bound to read a complete line. +1. – JackCColeman Aug 20 '13 at 07:56
  • @SakthiKumar : A normal one, yes. A one in a computer game can be much larger. :) – Daniel Kamil Kozar Aug 20 '13 at 08:25
0
int row, col;
char cmd;

char *s = NULL;
int slen = 0;
if (getline(&s, &slen, stdin) != -1) {
    if (sscanf(s, "%d %d", &row, &col) == 2) {
        free(s);
        // use row and col
    }
    else if (sscanf(s, "%c", &cmd) == 1) {
       free(s);
       // use cmd
    }
    else {
       // error
    }
}
Joker_vD
  • 3,715
  • 1
  • 28
  • 42
  • Hm, indeed. It turns out all our C projects where I work include an old utility file which provides `getline()`. The realization that `stdio` can't just read a line in one piece and return it really upset me. – Joker_vD Aug 20 '13 at 07:42
  • +1 I used the `IFs` and `sscanf` solution together with @Daniel's answer – Rodrigo Souza Aug 20 '13 at 20:49
-2

P.S.: those who did not read and understand my answer carefully, please respect yourself, DO NOT VOTE-DOWN AT WILL!

Beside "get the whole line and then use sscanf", read char by char until '\n' was entered is also a better way. If the program encountered 'h' or 'q', it could do the relevant action immediately, meanwhile you cloud also provide a realtime analysis for the input stream.

example:

#define ROW_IDX 0
#define COL_IDX 1
    int c;
    int buffer[2] = {0,0};
    int buff_pos;
    while( (c = getchar())) {
        if (c == '\n') {
            //a line was finished
            /* 
            row = buffer[ROW_IDX];
            col = buffer[COL_IDX];
            */
            buff_pos = 0;
            memset(buffer , 0 , sizeof(buffer));//clear the buffer after do sth...
        } else if (c == 'h') {
            //help
        } else if (c == 'q') {
            //quit
        } else {
            //assume the input is valid number, u'd better verify whether input is between '0' and '9'
            if (c == ' ') {
                //meet whitespace, switch the buffer from 'row' to 'col'
                ++buff_pos;
            } else {
                buffer[buff_pos%2] *= 10;
                buffer[buff_pos%2] += c - '0';
            }
        }
    }
House.Lee
  • 241
  • 1
  • 3
  • 12