¿Scanf se cierra cuando lee una nueva línea?

Si ingreso 5 5 en el terminal, presione enter, y presione enter nuevamente, quiero salir del ciclo.

 int readCoefficents(double complex *c){ int i = 0; double real; double img; while(scanf("%f %f", &real, &img) == 2) c[i++] = real + img * I; c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 return i; } 

Obviamente, ese código no está haciendo el trabajo por mí (y sí, sé que hay un desbordamiento de búfer esperando a suceder).

scanf no se cerrará a menos que escriba una letra (o alguna cadena no numérica, no blanca). ¿Cómo obtengo scanf para cerrar después de leer una línea vacía?

Use fgets para leer la entrada de la consola:

  int res = 2; while (res == 2) { char buf[100]; fgets(buf, sizeof(buf), stdin); res = sscanf(buf, "%f %f", &real, &img); if (res == 2) c[i++] = real + img * I; } c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 return i; 

El problema específico que tiene es que una cadena de formato de escaneo de %f omitirá el espacio en blanco ( incluidas las líneas nuevas) hasta que encuentre un personaje real para escanear. Desde el estándar c99:

Una especificación de conversión se ejecuta en los siguientes pasos:
– Los caracteres de entrada de espacio en blanco (especificados por la función isspace ) se omiten, a menos que la especificación incluya un especificador '[' , 'c' o 'n' .

y, en otro lugar, describiendo isspace() :

Los caracteres de espacio en blanco estándar son los siguientes: espacio ' ' , alimentación de formulario '\f' , nueva línea '\n' , retorno de carro '\r' , pestaña horizontal '\t' , y pestaña vertical '\v' .

Su mejor opción es usar fgets para obtener la línea (y esto puede protegerse del desbordamiento del búfer muy fácilmente), luego use sscanf en la línea resultante.

La función scanf es una de esas que deberías mirar con mucha cautela. La siguiente pieza de código es una que uso a menudo para manejar la entrada de línea:

 #include  #include  #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; } 

Probándolo con varias combinaciones:

 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] 

Lo que haría es usar esta función para obtener una línea de forma segura, luego simplemente use:

 sscanf (buffer, "%f %f", &real, &img) 

para obtener los valores reales (y verificar el recuento).


De hecho, aquí hay un progtwig completo que está más cerca de lo que desea:

 #include  #include  #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; } 

junto con una ejecución de prueba:

 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> _ 

Hay una forma de hacer lo que quieras usando solo scanf:

 int readCoefficents(double complex *c) { int i = 0; double real; double img; char buf[2]; while (scanf("%1[\n]", buf) == 0) { // loop until a blank line or EOF if (scanf("%lf %lf", &real, &img) == 2) // read two floats c[i++] = real + img * I; scanf("%*[^\n]"); // skip the rest of the line scanf("%*1[\n]"); // and the newline } c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 return i; } 

Si el usuario solo ingresa 1 flotador en una línea, leerá la siguiente línea para el segundo valor. Si se ingresa cualquier basura al azar, se saltará a una nueva línea e intentará nuevamente con la siguiente línea. De lo contrario, continuará leyendo pares de valores flotantes hasta que el usuario ingrese una línea en blanco o se scope un EOF.

Solución re PAXDIABLO: no funciona correctamente con la línea EMPTY ingresada por el usuario, por lo que esta línea se agregará en su función getLine ()

 if (strlen(buff) <= 1) return NO_INPUT; 

después de la línea:

 if (fgets (buff, sz, stdin) == NULL) return NO_INPUT; 

Entonces se convertirá en:

 ... if (strlen(buff) <= 1) return NO_INPUT; if (fgets (buff, sz, stdin) == NULL) return NO_INPUT; ....