The specific problem you're having is that a scanf
format string of %f
will skip white space (including newlines) until it finds an actual character to scan. From the c99 standard:
A conversion specification is executed in the following steps:
- Input white-space characters (as specified by the isspace
function) are skipped, unless the specification includes a '['
, 'c'
, or 'n'
specifier.
and, elsewhere, describing isspace()
:
The standard white-space characters are the following: space ' '
, form feed '\f'
, new-line '\n'
, carriage return '\r'
, horizontal tab '\t'
, and vertical tab '\v'
.
Your best bet is to use fgets
to get the line (and this can be protected from buffer overflow very easily), then use sscanf
on the resultant line.
The scanf
function is one of those ones you should look at very warily. The following piece of code is one I often use to handle line input:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Testing it with various combinations:
pax> ./prog
Enter string>[CTRL-D]
No input
pax> ./prog
Enter string> a
OK [a]
pax> ./prog
Enter string> hello
OK [hello]
pax> ./prog
Enter string> hello there
Input too long [hello the]
pax> ./prog
Enter string> i am pax
OK [i am pax]
What I would do is to use this function to get a line safely, then simply use:
sscanf (buffer, "%f %f", &real, &img)
to get the actual values (and check the count).
In fact, here's a complete program which is closer to what you want:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main (void) {
int i = 1, rc;
char prompt[50], buff[50];
float real, imag;
while (1) {
sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i);
rc = getLine (prompt, buff, sizeof(buff));
if (rc == NO_INPUT) break;
if (*buff == '\0') break;
if (rc == TOO_LONG) {
printf ("** Input too long [%s]...\n", buff);
}
if (sscanf (buff, "%f %f", &real, &imag) == 2) {
printf ("Values were %f and %f\n", real, imag);
i++;
} else {
printf ("** Invalid input [%s]\n", buff);
}
}
return 0;
}
along with a test run:
pax> ./testprog
Enter real and imaginary for # 1: hello
** Invalid input [hello]
Enter real and imaginary for # 1: hello there
** Invalid input [hello there]
Enter real and imaginary for # 1: 1
** Invalid input [1]
Enter real and imaginary for # 1: 1.23 4.56
Values were 1.230000 and 4.560000
Enter real and imaginary for # 2:
pax> _