Why don't you try fgets()
(with the standard input file stdin
)?
fgets()
lets you to specify the maximum size for your buffer.
(In all what follows, I'll be using standard ISO C99 compatible
syntax.)
Thus, you can write this code:
#include <stdio.h>
#define MAXBUFF 20 /* Small just for testing... */
int main(void) {
char buffer[MAXBUFF+1]; /* Add 1 byte since fgets() inserts '\0' at end */
fgets(buffer, MAXBUFF+1, stdin);
printf("Your input was: %s\n", buffer);
return 0;
}
fgets()
reads at most MAXBUFF characters from stdin
,
which is the standard input (that means: the keyboard).
The result is held in the array buffer
.
If a '\n' character is found, the reading stops and '\n' is also held in buffer
(as the last character). In addition, always a '\0' is added at the end of buffer
, so enough storage is needed.
You can use a combination of fgets()
followed by sscanf()
in order to process the string:
char buffer[MAXBUFF+1];
fgets(buffer, MAXBUFF+1, stdin); /* Plain read */
int x; float f;
sscanf(buffer, "%d %g", &x, &f); /* Specialized read */
Thus, you have a "safe" scanf()
-like method.
Note: This approach has a potencial problem. If fgets()
reachs MAXBUFF characters before the end-of-line character '\n' is obtained, the rest of the input will not be discarded, and it will be taken as part of the next keyboard reading.
Hence, one has to add a flush mechanism, that actually is very simple:
while(getchar()!'\n')
; /* Flushing stdin... */
However: If you just add that last piece of code after the fgets()
line,
the user will be forced two press ENTER two times each time (s)he enters less than MAXBUFF characters. Worst: this is the most typical situation!
To fix this new problem, observe that an easy logical condition completeley equivalent to the fact that the character '\n' was not reached, is the following:
(buffer[MAXBUFF - 1] != '\0') && (buffer[MAXBUFF - 1] != '\n')
(Prove it!)
Thus, we write:
fgets(buffer, maxb+1, stdin);
if ((buffer[MAXBUFF - 1] != '\0') && (buffer[MAXBUFF - 1] != '\n'))
while(getchar() != '\n')
;
A final touch is needed: since the array buffer could have garbadge,
it seems that some kind of initialization is needed.
However, let us observe that only the position [MAXBUFF - 1]
has to be cleaned:
char buffer[MAXBUFF + 1] = { [MAXBUFF - 1] = '\0' }; /* ISO C99 syntax */
Finally, we can gather all that facts in a quick macro, like this program shows:
#include <stdio.h>
#define safe_scanf(fmt, maxb, ...) { \
char buffer[maxb+1] = { [maxb - 1] = '\0' }; \
fgets(buffer, maxb+1, stdin); \
if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \
while(getchar() != '\n') \
; \
sscanf(buffer, fmt, __VA_ARGS__); \
}
#define MAXBUFF 20
int main(void) {
int x; float f;
safe_scanf("%d %g", MAXBUFF+1, &x, &f);
printf("Your input was: x == %d\t\t f == %g", x, f);
return 0;
}
It has been used the mechanism of variable number of parameters in a macro,
under the ISO C99 norms: Variadic macros
__VA_ARGS__
replaces the variable list of parameters.
(We need variable number of parameters in order to mimic the scanf()
-like behaviour.)
Notes: The macro-body was enclosed inside a block with { }. This is not completely satisfactory, and it is easily improved, but it is part of another topic...
In particular, the macro safe_scanf()
does not "return" a value (it is not an expression, but a block statement).
Remark: Inside the macro I have declared an array buffer
which is created at the time of entering the block, and then is destroyed when the block is exited. The scope of buffer
is limited to the block of the macro.